@proteinjs/db-driver-spanner 1.16.2 → 1.17.0
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 +28 -0
- package/dist/test/MigrationRunner.test.d.ts.map +1 -1
- package/dist/test/MigrationRunner.test.js +127 -1
- package/dist/test/MigrationRunner.test.js.map +1 -1
- package/dist/test/SessionRecordTableQuery.test.js +38 -0
- package/dist/test/SessionRecordTableQuery.test.js.map +1 -1
- package/dist/test/SpannerEmulatorProvisioner.test.js +65 -42
- package/dist/test/SpannerEmulatorProvisioner.test.js.map +1 -1
- package/package.json +6 -6
- package/test/MigrationRunner.test.ts +104 -2
- package/test/SessionRecordTableQuery.test.ts +28 -0
- package/test/SpannerEmulatorProvisioner.test.ts +63 -15
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.17.0](https://github.com/proteinjs/db/compare/@proteinjs/db-driver-spanner@1.16.3...@proteinjs/db-driver-spanner@1.17.0) (2026-08-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **test:** abort-aware DDL-rejection control in the provisioner re-ensure suite ([8418cb2](https://github.com/proteinjs/db/commit/8418cb20609d279b35e654948540d690a79b6923))
|
|
12
|
+
* **test:** provisioner control detects emulator DDL-rejection semantics ([3da031a](https://github.com/proteinjs/db/commit/3da031a4a74f172b1dc7df73d273be028c900508))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* ensureMigrationRun — system-context boot API for deploy-coupled migrations ([d771c7b](https://github.com/proteinjs/db/commit/d771c7bc8958ebd38e15b1fd86fde19267ec3d9f))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## [1.16.3](https://github.com/proteinjs/db/compare/@proteinjs/db-driver-spanner@1.16.2...@proteinjs/db-driver-spanner@1.16.3) (2026-08-14)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* condition binds normalize Moment values at the parameterize boundary — the home-recents TIMESTAMP 400 ([812d74e](https://github.com/proteinjs/db/commit/812d74e3ddf69e77fa4cf343dc7e8064ebcfc263))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
6
34
|
## [1.16.2](https://github.com/proteinjs/db/compare/@proteinjs/db-driver-spanner@1.16.1...@proteinjs/db-driver-spanner@1.16.2) (2026-08-13)
|
|
7
35
|
|
|
8
36
|
**Note:** Version bump only for package @proteinjs/db-driver-spanner
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationRunner.test.d.ts","sourceRoot":"","sources":["../../test/MigrationRunner.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MigrationRunner.test.d.ts","sourceRoot":"","sources":["../../test/MigrationRunner.test.ts"],"names":[],"mappings":"AAaA,OAAO,yBAAyB,CAAC"}
|
|
@@ -77,6 +77,46 @@ describe('MigrationRunner (spanner)', function () {
|
|
|
77
77
|
return [2 /*return*/, 'migration output'];
|
|
78
78
|
}); }); },
|
|
79
79
|
};
|
|
80
|
+
// ensureMigrationRun fixtures carry run counters: re-running any of them is VISIBLE as a
|
|
81
|
+
// counter increment, so idempotence and retry are asserted on the migration's own effect,
|
|
82
|
+
// not on bookkeeping interactions.
|
|
83
|
+
var ensureBootRunCount = 0;
|
|
84
|
+
var ensureBootMigration = {
|
|
85
|
+
id: 'migration-runner-test-ensure-boot',
|
|
86
|
+
description: 'runs at boot with no user session',
|
|
87
|
+
run: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
88
|
+
return __generator(this, function (_a) {
|
|
89
|
+
ensureBootRunCount++;
|
|
90
|
+
return [2 /*return*/, 'boot output'];
|
|
91
|
+
});
|
|
92
|
+
}); },
|
|
93
|
+
};
|
|
94
|
+
var ensureIdempotentRunCount = 0;
|
|
95
|
+
var ensureIdempotentMigration = {
|
|
96
|
+
// Ids stay <= 36 chars — the migration table's id column is uuid-width.
|
|
97
|
+
id: 'migration-runner-test-ensure-skip',
|
|
98
|
+
description: 'must not re-run once successful',
|
|
99
|
+
run: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
100
|
+
return __generator(this, function (_a) {
|
|
101
|
+
ensureIdempotentRunCount++;
|
|
102
|
+
return [2 /*return*/, 'idempotent output'];
|
|
103
|
+
});
|
|
104
|
+
}); },
|
|
105
|
+
};
|
|
106
|
+
var ensureRetryAttempts = 0;
|
|
107
|
+
var ensureRetriedMigration = {
|
|
108
|
+
id: 'migration-runner-test-ensure-retried',
|
|
109
|
+
description: 'fails first, fixed on retry',
|
|
110
|
+
run: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
111
|
+
return __generator(this, function (_a) {
|
|
112
|
+
ensureRetryAttempts++;
|
|
113
|
+
if (ensureRetryAttempts === 1) {
|
|
114
|
+
throw new Error('ensure migration blew up mid-run');
|
|
115
|
+
}
|
|
116
|
+
return [2 /*return*/, 'fixed on retry'];
|
|
117
|
+
});
|
|
118
|
+
}); },
|
|
119
|
+
};
|
|
80
120
|
beforeAll(function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
81
121
|
var _i, _a, migration;
|
|
82
122
|
return __generator(this, function (_b) {
|
|
@@ -99,7 +139,13 @@ describe('MigrationRunner (spanner)', function () {
|
|
|
99
139
|
return [4 /*yield*/, spannerDriver.getTableManager().loadTable(migrationTable)];
|
|
100
140
|
case 3:
|
|
101
141
|
_b.sent();
|
|
102
|
-
_i = 0, _a = [
|
|
142
|
+
_i = 0, _a = [
|
|
143
|
+
failingMigration,
|
|
144
|
+
succeedingMigration,
|
|
145
|
+
ensureBootMigration,
|
|
146
|
+
ensureIdempotentMigration,
|
|
147
|
+
ensureRetriedMigration,
|
|
148
|
+
];
|
|
103
149
|
_b.label = 4;
|
|
104
150
|
case 4:
|
|
105
151
|
if (!(_i < _a.length)) return [3 /*break*/, 7];
|
|
@@ -168,5 +214,85 @@ describe('MigrationRunner (spanner)', function () {
|
|
|
168
214
|
}
|
|
169
215
|
});
|
|
170
216
|
}); });
|
|
217
|
+
describe('ensureMigrationRun (boot path)', function () {
|
|
218
|
+
// Boot context: NO user session exists — UserAuth is fail-closed, so any getDb() bookkeeping
|
|
219
|
+
// would deny. These tests run with the suite's test identity CLEARED to pin that
|
|
220
|
+
// ensureMigrationRun records through the system db. Assertions read as system for the same
|
|
221
|
+
// reason; the identity is restored for the rest of the suite (afterAll drops the table via
|
|
222
|
+
// getDb-adjacent paths).
|
|
223
|
+
beforeEach(function () { return (0, test_1.clearTestUser)(); });
|
|
224
|
+
afterEach(function () { return (0, test_1.registerTestUser)(); });
|
|
225
|
+
test('runs a pending migration with no user session and records success', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
226
|
+
var runner, row;
|
|
227
|
+
return __generator(this, function (_a) {
|
|
228
|
+
switch (_a.label) {
|
|
229
|
+
case 0:
|
|
230
|
+
runner = new db_1.MigrationRunner();
|
|
231
|
+
return [4 /*yield*/, runner.ensureMigrationRun(ensureBootMigration.id)];
|
|
232
|
+
case 1:
|
|
233
|
+
_a.sent();
|
|
234
|
+
expect(ensureBootRunCount).toBe(1);
|
|
235
|
+
return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(migrationTable, { id: ensureBootMigration.id })];
|
|
236
|
+
case 2:
|
|
237
|
+
row = _a.sent();
|
|
238
|
+
expect(row.status).toBe('success');
|
|
239
|
+
expect(row.output).toBe('boot output');
|
|
240
|
+
expect(row.duration).toBeTruthy();
|
|
241
|
+
return [2 /*return*/];
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}); });
|
|
245
|
+
test('a second call skips an already-successful migration', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
246
|
+
var runner, row;
|
|
247
|
+
return __generator(this, function (_a) {
|
|
248
|
+
switch (_a.label) {
|
|
249
|
+
case 0:
|
|
250
|
+
runner = new db_1.MigrationRunner();
|
|
251
|
+
return [4 /*yield*/, runner.ensureMigrationRun(ensureIdempotentMigration.id)];
|
|
252
|
+
case 1:
|
|
253
|
+
_a.sent();
|
|
254
|
+
expect(ensureIdempotentRunCount).toBe(1);
|
|
255
|
+
return [4 /*yield*/, runner.ensureMigrationRun(ensureIdempotentMigration.id)];
|
|
256
|
+
case 2:
|
|
257
|
+
_a.sent();
|
|
258
|
+
// The migration's effect did not repeat — 'ensure' skipped the successful row.
|
|
259
|
+
expect(ensureIdempotentRunCount).toBe(1);
|
|
260
|
+
return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(migrationTable, { id: ensureIdempotentMigration.id })];
|
|
261
|
+
case 3:
|
|
262
|
+
row = _a.sent();
|
|
263
|
+
expect(row.status).toBe('success');
|
|
264
|
+
return [2 /*return*/];
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}); });
|
|
268
|
+
test('a throwing migration records failure and is retried by the next call', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
269
|
+
var runner, failedRow, retriedRow;
|
|
270
|
+
return __generator(this, function (_a) {
|
|
271
|
+
switch (_a.label) {
|
|
272
|
+
case 0:
|
|
273
|
+
runner = new db_1.MigrationRunner();
|
|
274
|
+
return [4 /*yield*/, runner.ensureMigrationRun(ensureRetriedMigration.id)];
|
|
275
|
+
case 1:
|
|
276
|
+
_a.sent();
|
|
277
|
+
expect(ensureRetryAttempts).toBe(1);
|
|
278
|
+
return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(migrationTable, { id: ensureRetriedMigration.id })];
|
|
279
|
+
case 2:
|
|
280
|
+
failedRow = _a.sent();
|
|
281
|
+
expect(failedRow.status).toBe('failure');
|
|
282
|
+
expect(failedRow.failureMessage).toBe('ensure migration blew up mid-run');
|
|
283
|
+
return [4 /*yield*/, runner.ensureMigrationRun(ensureRetriedMigration.id)];
|
|
284
|
+
case 3:
|
|
285
|
+
_a.sent();
|
|
286
|
+
expect(ensureRetryAttempts).toBe(2);
|
|
287
|
+
return [4 /*yield*/, (0, db_1.getDbAsSystem)().get(migrationTable, { id: ensureRetriedMigration.id })];
|
|
288
|
+
case 4:
|
|
289
|
+
retriedRow = _a.sent();
|
|
290
|
+
expect(retriedRow.status).toBe('success');
|
|
291
|
+
expect(retriedRow.output).toBe('fixed on retry');
|
|
292
|
+
return [2 /*return*/];
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}); });
|
|
296
|
+
});
|
|
171
297
|
});
|
|
172
298
|
//# sourceMappingURL=MigrationRunner.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationRunner.test.js","sourceRoot":"","sources":["../../test/MigrationRunner.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kEAA6D;AAC7D,
|
|
1
|
+
{"version":3,"file":"MigrationRunner.test.js","sourceRoot":"","sources":["../../test/MigrationRunner.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -188,5 +188,43 @@ describe('Session-shaped record table query (the admin Sessions table path)', fu
|
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
190
|
}); }, 60000);
|
|
191
|
+
test('a Moment condition value binds as TIMESTAMP (the cursor-window query shape)', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
192
|
+
var band, n, qb, rows, inQb, inRows;
|
|
193
|
+
return __generator(this, function (_a) {
|
|
194
|
+
switch (_a.label) {
|
|
195
|
+
case 0:
|
|
196
|
+
band = Date.now() + 600000;
|
|
197
|
+
n = 4;
|
|
198
|
+
_a.label = 1;
|
|
199
|
+
case 1:
|
|
200
|
+
if (!(n <= 6)) return [3 /*break*/, 4];
|
|
201
|
+
return [4 /*yield*/, systemDb.insert(table, storeShapedRow(n, band + (n - 3) * 1000))];
|
|
202
|
+
case 2:
|
|
203
|
+
_a.sent();
|
|
204
|
+
_a.label = 3;
|
|
205
|
+
case 3:
|
|
206
|
+
n++;
|
|
207
|
+
return [3 /*break*/, 1];
|
|
208
|
+
case 4:
|
|
209
|
+
qb = new db_1.QueryBuilderFactory()
|
|
210
|
+
.createQueryBuilder(table)
|
|
211
|
+
.condition({ field: 'updated', operator: '>', value: (0, moment_1.default)(band + 500) })
|
|
212
|
+
.condition({ field: 'updated', operator: '<', value: (0, moment_1.default)(band + 2500) })
|
|
213
|
+
.sort([{ field: 'updated', desc: true }]);
|
|
214
|
+
return [4 /*yield*/, db.query(table, qb)];
|
|
215
|
+
case 5:
|
|
216
|
+
rows = _a.sent();
|
|
217
|
+
expect(rows.map(function (row) { return row.sessionId; })).toEqual(['test-session-5', 'test-session-4']);
|
|
218
|
+
inQb = new db_1.QueryBuilderFactory()
|
|
219
|
+
.createQueryBuilder(table)
|
|
220
|
+
.condition({ field: 'updated', operator: 'IN', value: [(0, moment_1.default)(band + 1000), (0, moment_1.default)(band + 3000)] });
|
|
221
|
+
return [4 /*yield*/, db.query(table, inQb)];
|
|
222
|
+
case 6:
|
|
223
|
+
inRows = _a.sent();
|
|
224
|
+
expect(inRows.map(function (row) { return row.sessionId; }).sort()).toEqual(['test-session-4', 'test-session-6']);
|
|
225
|
+
return [2 /*return*/];
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}); }, 60000);
|
|
191
229
|
});
|
|
192
230
|
//# sourceMappingURL=SessionRecordTableQuery.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionRecordTableQuery.test.js","sourceRoot":"","sources":["../../test/SessionRecordTableQuery.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionRecordTableQuery.test.js","sourceRoot":"","sources":["../../test/SessionRecordTableQuery.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,kDAA4B;AAC5B,kEAA6D;AAC7D,oCAAoH;AACpH,2CAAqE;AACrE,4EAAuE;AACvE,4DAA2D;AAC3D,gFAA+E;AAC/E,mCAAiC;AAsBjC,2FAA2F;AAC3F;IAAgC,qCAAmB;IAAnD;QAAA,qEAQC;QAPC,UAAI,GAAG,oCAAoC,CAAC;QAC5C,aAAO,GAAG,IAAA,sBAAiB,EAAe;YACxC,SAAS,EAAE,IAAI,iBAAY,CAAC,YAAY,CAAC;YACzC,OAAO,EAAE,IAAI,iBAAY,CAAC,oBAAoB,EAAE,EAAE,EAAE,IAAI,CAAC;YACzD,OAAO,EAAE,IAAI,eAAU,CAAC,SAAS,CAAC;YAClC,SAAS,EAAE,IAAI,iBAAY,CAAC,YAAY,CAAC;SAC1C,CAAC,CAAC;;IACL,CAAC;IAAD,wBAAC;AAAD,CAAC,AARD,CAAgC,UAAK,GAQpC;AAED,IAAM,KAAK,GAAG,IAAI,iBAAiB,EAAE,CAAC;AACtC,IAAM,QAAQ,GAAG,UAAC,SAAiB;IACjC,IAAI,SAAS,KAAK,KAAK,CAAC,IAAI,EAAE;QAC5B,OAAO,KAAK,CAAC;KACd;IACD,MAAM,IAAI,KAAK,CAAC,2CAAoC,SAAS,CAAE,CAAC,CAAC;AACnE,CAAC,CAAC;AAEF,IAAM,aAAa,GAAG,IAAI,iCAAa,CACrC;IACE,SAAS,EAAE,gBAAgB;IAC3B,YAAY,EAAE,gBAAgB;IAC9B,YAAY,EAAE,MAAM;CACrB,EACD,QAAQ,CACT,CAAC;AAEF;;;;GAIG;AACH,IAAM,cAAc,GAAG,UAAC,CAAS,EAAE,SAAiB,IAAK,OAAA,CAAC;IACxD,SAAS,EAAE,uBAAgB,CAAC,CAAE;IAC9B,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;QACtB,MAAM,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,0BAA0B,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;QACtG,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAQ,CAAC,gBAAa,EAAE;KAC3C,CAAC;IACF,OAAO,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC;IAC1C,SAAS,EAAE,eAAQ,CAAC,gBAAa;IACjC,OAAO,EAAE,IAAA,gBAAM,EAAC,SAAS,CAAC;CAC3B,CAAC,EATuD,CASvD,CAAC;AAEH,QAAQ,CAAC,mEAAmE,EAAE;IAC5E,IAAM,SAAS,GAAG,IAAA,mCAAgB,EAAC,aAAa,CAAC,CAAC;IAClD,mFAAmF;IACnF,IAAM,QAAQ,GAAG,IAAI,OAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,IAAI,2CAAkB,EAAE,EAAE,IAAI,CAAC,CAAC;IACjF,IAAM,EAAE,GAAG,IAAI,OAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,IAAI,2CAAkB,EAAE,CAAC,CAAC;IAErE,SAAS,CAAC;;;;oBACR,4FAA4F;oBAC5F,2FAA2F;oBAC3F,IAAA,uBAAgB,GAAE,CAAC;oBACnB,qBAAM,uDAA0B,CAAC,iBAAiB,CAAC;4BACjD,SAAS,EAAE,gBAAgB;4BAC3B,YAAY,EAAE,gBAAgB;4BAC9B,YAAY,EAAE,MAAM;yBACrB,CAAC,EAAA;;oBAJF,SAIE,CAAC;oBACH,qBAAM,SAAS,CAAC,KAAK,CAAC,EAAA;;oBAAtB,SAAsB,CAAC;oBACvB,qBAAM,aAAa,CAAC,eAAe,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAA;;oBAAtD,SAAsD,CAAC;;;;SACxD,EAAE,KAAK,CAAC,CAAC;IAEV,QAAQ,CAAC;;;;oBACP,IAAA,oBAAa,GAAE,CAAC;oBAChB,qBAAM,SAAS,CAAC,KAAK,CAAC,EAAA;;oBAAtB,SAAsB,CAAC;oBACvB,qBAAM,uDAA0B,CAAC,OAAO,EAAE,EAAA;;oBAA1C,SAA0C,CAAC;;;;SAC5C,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,uFAAuF,EAAE;;;;;oBAEtF,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAM,CAAC;oBACxB,CAAC,GAAG,CAAC;;;yBAAE,CAAA,CAAC,IAAI,CAAC,CAAA;oBACpB,qBAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,IAAI,CAAQ,CAAC,EAAA;;oBAAvE,SAAuE,CAAC;;;oBADlD,CAAC,EAAE,CAAA;;;oBAKrB,EAAE,GAAG,IAAI,wBAAmB,EAAE;yBACjC,kBAAkB,CAAe,KAAK,CAAC;yBACvC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;yBACxC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;oBACtB,qBAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,EAAA;;oBAAhC,IAAI,GAAG,SAAyB;oBAEtC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAC,GAAG,IAAK,OAAA,GAAG,CAAC,SAAS,EAAb,CAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC;oBACzG,qEAAqE;oBACrE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;oBACpD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;oBAC5E,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC;oBAG/B,OAAO,GAAG,IAAI,wBAAmB,EAAE,CAAC,kBAAkB,CAAe,KAAK,CAAC,CAAC;oBAClF,KAAA,MAAM,CAAA;oBAAC,qBAAM,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,EAAA;;oBAA3C,kBAAO,SAAoC,EAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;;;SACtD,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,6EAA6E,EAAE;;;;;oBAO5E,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAO,CAAC;oBACzB,CAAC,GAAG,CAAC;;;yBAAE,CAAA,CAAC,IAAI,CAAC,CAAA;oBACpB,qBAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAQ,CAAC,EAAA;;oBAA7E,SAA6E,CAAC;;;oBADxD,CAAC,EAAE,CAAA;;;oBAIrB,EAAE,GAAG,IAAI,wBAAmB,EAAE;yBACjC,kBAAkB,CAAe,KAAK,CAAC;yBACvC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAA,gBAAM,EAAC,IAAI,GAAG,GAAG,CAAQ,EAAE,CAAC;yBAChF,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAA,gBAAM,EAAC,IAAI,GAAG,IAAI,CAAQ,EAAE,CAAC;yBACjF,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;oBAC/B,qBAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,EAAA;;oBAAhC,IAAI,GAAG,SAAyB;oBACtC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAC,GAAG,IAAK,OAAA,GAAG,CAAC,SAAS,EAAb,CAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC;oBAGjF,IAAI,GAAG,IAAI,wBAAmB,EAAE;yBACnC,kBAAkB,CAAe,KAAK,CAAC;yBACvC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAA,gBAAM,EAAC,IAAI,GAAG,IAAI,CAAC,EAAE,IAAA,gBAAM,EAAC,IAAI,GAAG,IAAI,CAAC,CAAQ,EAAE,CAAC,CAAC;oBAC9F,qBAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAA;;oBAApC,MAAM,GAAG,SAA2B;oBAC1C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAC,GAAG,IAAK,OAAA,GAAG,CAAC,SAAS,EAAb,CAAa,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC;;;;SACjG,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC,CAAC,CAAC"}
|
|
@@ -116,7 +116,7 @@ describe('SpannerEmulatorProvisioner', function () {
|
|
|
116
116
|
});
|
|
117
117
|
}); }, 30000);
|
|
118
118
|
test('re-ensure issues zero DDL: succeeds while an open read-write transaction rejects all schema changes', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
119
|
-
var tables, createOp, transaction, rows;
|
|
119
|
+
var tables, createOp, transaction, controlHeld, rejectionSemanticsAbsent, attempt, op, error_1, rows;
|
|
120
120
|
return __generator(this, function (_a) {
|
|
121
121
|
switch (_a.label) {
|
|
122
122
|
case 0: return [4 /*yield*/, database.run({
|
|
@@ -133,63 +133,86 @@ describe('SpannerEmulatorProvisioner', function () {
|
|
|
133
133
|
case 3:
|
|
134
134
|
_a.sent();
|
|
135
135
|
_a.label = 4;
|
|
136
|
-
case 4:
|
|
136
|
+
case 4:
|
|
137
|
+
_a.trys.push([4, , 20, 23]);
|
|
138
|
+
controlHeld = false;
|
|
139
|
+
rejectionSemanticsAbsent = false;
|
|
140
|
+
attempt = 0;
|
|
141
|
+
_a.label = 5;
|
|
137
142
|
case 5:
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
if (!(attempt < 3 && !controlHeld && !rejectionSemanticsAbsent)) return [3 /*break*/, 17];
|
|
144
|
+
if (!transaction) return [3 /*break*/, 7];
|
|
145
|
+
return [4 /*yield*/, transaction.rollback().catch(function () { return undefined; })];
|
|
140
146
|
case 6:
|
|
141
|
-
_a.
|
|
147
|
+
_a.sent();
|
|
148
|
+
transaction.end();
|
|
149
|
+
_a.label = 7;
|
|
150
|
+
case 7: return [4 /*yield*/, database.getTransaction()];
|
|
151
|
+
case 8:
|
|
152
|
+
transaction = (_a.sent())[0];
|
|
142
153
|
return [4 /*yield*/, transaction.runUpdate({
|
|
143
154
|
sql: "INSERT INTO ".concat(SCRATCH_TABLE, " (id) VALUES (@id)"),
|
|
144
|
-
params: { id: "re-ensure-".concat(Date.now()) },
|
|
155
|
+
params: { id: "re-ensure-".concat(attempt, "-").concat(Date.now()) },
|
|
145
156
|
})];
|
|
146
|
-
case
|
|
157
|
+
case 9:
|
|
147
158
|
_a.sent();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
case 0: return [4 /*yield*/, database.updateSchema("ALTER DATABASE `".concat(spannerConfig.databaseName, "` SET OPTIONS (version_retention_period = '1m')"))];
|
|
157
|
-
case 1:
|
|
158
|
-
op = (_a.sent())[0];
|
|
159
|
-
return [4 /*yield*/, op.promise()];
|
|
160
|
-
case 2:
|
|
161
|
-
_a.sent();
|
|
162
|
-
return [2 /*return*/];
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
}); })()).rejects.toMatchObject({ code: 9 /* gRPC FAILED_PRECONDITION */ })];
|
|
166
|
-
case 8:
|
|
167
|
-
// Control leg: the open transaction really does reject DDL in this window — the same
|
|
168
|
-
// rejection the pre-fix unconditional pin ALTER died on. If the emulator ever stops
|
|
169
|
-
// rejecting schema changes under an open transaction, this control fails and flags that
|
|
170
|
-
// the invariant leg below has lost its bite.
|
|
159
|
+
_a.label = 10;
|
|
160
|
+
case 10:
|
|
161
|
+
_a.trys.push([10, 13, , 14]);
|
|
162
|
+
return [4 /*yield*/, database.updateSchema("ALTER DATABASE `".concat(spannerConfig.databaseName, "` SET OPTIONS (version_retention_period = '1m')"))];
|
|
163
|
+
case 11:
|
|
164
|
+
op = (_a.sent())[0];
|
|
165
|
+
return [4 /*yield*/, op.promise()];
|
|
166
|
+
case 12:
|
|
171
167
|
_a.sent();
|
|
168
|
+
return [3 /*break*/, 14];
|
|
169
|
+
case 13:
|
|
170
|
+
error_1 = _a.sent();
|
|
171
|
+
expect(error_1).toMatchObject({ code: 9 /* gRPC FAILED_PRECONDITION */ });
|
|
172
|
+
controlHeld = true;
|
|
173
|
+
return [3 /*break*/, 14];
|
|
174
|
+
case 14:
|
|
175
|
+
if (controlHeld) {
|
|
176
|
+
return [3 /*break*/, 17];
|
|
177
|
+
}
|
|
178
|
+
return [4 /*yield*/, transaction.commit().then(function () { return true; }, function () { return false; })];
|
|
179
|
+
case 15:
|
|
180
|
+
rejectionSemanticsAbsent = _a.sent();
|
|
181
|
+
_a.label = 16;
|
|
182
|
+
case 16:
|
|
183
|
+
attempt++;
|
|
184
|
+
return [3 /*break*/, 5];
|
|
185
|
+
case 17:
|
|
186
|
+
if (rejectionSemanticsAbsent) {
|
|
187
|
+
// DDL succeeded against a verifiably-active transaction: this emulator does not
|
|
188
|
+
// reject schema changes under open transactions, so a stranded transaction cannot
|
|
189
|
+
// fail a re-ensure here — the invariant's failure class is absent by construction.
|
|
190
|
+
// The reconcile leg below still bites: re-ensure resolves and retention stays pinned.
|
|
191
|
+
transaction === null || transaction === void 0 ? void 0 : transaction.end();
|
|
192
|
+
transaction = undefined;
|
|
193
|
+
}
|
|
194
|
+
else if (!controlHeld) {
|
|
195
|
+
throw new Error('control could not hold an active transaction across 3 attempts — ambient writers kept aborting it');
|
|
196
|
+
}
|
|
172
197
|
// The invariant: re-ensure resolves inside the same window — it issued zero DDL.
|
|
173
198
|
return [4 /*yield*/, expect(SpannerEmulatorProvisioner_1.SpannerEmulatorProvisioner.ensureProvisioned(spannerConfig)).resolves.toBeUndefined()];
|
|
174
|
-
case
|
|
199
|
+
case 18:
|
|
175
200
|
// The invariant: re-ensure resolves inside the same window — it issued zero DDL.
|
|
176
201
|
_a.sent();
|
|
177
202
|
return [4 /*yield*/, retentionRows(database)];
|
|
178
|
-
case
|
|
203
|
+
case 19:
|
|
179
204
|
rows = _a.sent();
|
|
180
205
|
expect(rows).toEqual([{ OPTION_VALUE: '1m' }]);
|
|
181
|
-
return [3 /*break*/,
|
|
182
|
-
case
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
case 12:
|
|
187
|
-
// Never strand the transaction on the shared test emulator — that would poison later
|
|
188
|
-
// suites' DDL exactly like the class this test guards against.
|
|
206
|
+
return [3 /*break*/, 23];
|
|
207
|
+
case 20:
|
|
208
|
+
if (!transaction) return [3 /*break*/, 22];
|
|
209
|
+
return [4 /*yield*/, transaction.rollback().catch(function () { return undefined; })];
|
|
210
|
+
case 21:
|
|
189
211
|
_a.sent();
|
|
190
212
|
transaction.end();
|
|
191
|
-
|
|
192
|
-
case
|
|
213
|
+
_a.label = 22;
|
|
214
|
+
case 22: return [7 /*endfinally*/];
|
|
215
|
+
case 23: return [2 /*return*/];
|
|
193
216
|
}
|
|
194
217
|
});
|
|
195
218
|
}); }, 60000);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpannerEmulatorProvisioner.test.js","sourceRoot":"","sources":["../../test/SpannerEmulatorProvisioner.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAuE;AACvE,gFAA+E;AAE/E,IAAM,aAAa,GAAG;IACpB,SAAS,EAAE,gBAAgB;IAC3B,YAAY,EAAE,gBAAgB;IAC9B,YAAY,EAAE,MAAM;CACrB,CAAC;AAEF,IAAM,aAAa,GAAG,8BAA8B,CAAC;AAErD,IAAM,aAAa,GAAG,UAAO,QAAkB;;;;oBAC9B,qBAAM,QAAQ,CAAC,GAAG,CAAC;oBAChC,GAAG,EAAE,mHAAmH;oBACxH,IAAI,EAAE,IAAI;iBACX,CAAC,EAAA;;gBAHK,IAAI,GAAI,CAAA,SAGb,CAAA,GAHS;gBAIX,sBAAO,IAAkC,EAAC;;;KAC3C,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,QAAQ,CAAC,4BAA4B,EAAE;IACrC,IAAM,OAAO,GAAG,IAAI,iBAAO,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC;IACpE,IAAI,QAAkB,CAAC;IAEvB,SAAS,CAAC;;;;gBACR,mFAAmF;gBACnF,mFAAmF;gBACnF,qBAAM,uDAA0B,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAA;;oBAFjE,mFAAmF;oBACnF,mFAAmF;oBACnF,SAAiE,CAAC;oBAClE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;oBAC7F,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAM,OAAA,SAAS,EAAT,CAAS,CAAC,CAAC;;;;SACvC,EAAE,KAAM,CAAC,CAAC;IAEX,QAAQ,CAAC;;;wBACP,qBAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,cAAM,OAAA,SAAS,EAAT,CAAS,CAAC,EAAA;;oBAA7C,SAA6C,CAAC;oBAC9C,OAAO,CAAC,KAAK,EAAE,CAAC;oBAChB,uDAA0B,CAAC,OAAO,EAAE,CAAC;;;;SACtC,EAAE,KAAM,CAAC,CAAC;IAEX,IAAI,CAAC,+DAA+D,EAAE;;;;wBAGvD,qBAAM,aAAa,CAAC,QAAQ,CAAC,EAAA;;oBAApC,IAAI,GAAG,SAA6B;oBAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;;;;SAChD,EAAE,KAAM,CAAC,CAAC;IAEX,IAAI,CAAC,qGAAqG,EAAE;;;;wBAGzF,qBAAM,QAAQ,CAAC,GAAG,CAAC;wBAClC,GAAG,EAAE,6GAAsG,aAAa,MAAG;wBAC3H,IAAI,EAAE,IAAI;qBACX,CAAC,EAAA;;oBAHK,MAAM,GAAI,CAAA,SAGf,CAAA,GAHW;yBAIT,CAAA,MAAM,CAAC,MAAM,KAAK,CAAC,CAAA,EAAnB,wBAAmB;oBACF,qBAAM,QAAQ,CAAC,YAAY,CAAC,uBAAgB,aAAa,sCAAmC,CAAC,EAAA;;oBAAzG,QAAQ,GAAI,CAAA,SAA6F,CAAA,GAAjG;oBACf,qBAAM,QAAQ,CAAC,OAAO,EAAE,EAAA;;oBAAxB,SAAwB,CAAC;;
|
|
1
|
+
{"version":3,"file":"SpannerEmulatorProvisioner.test.js","sourceRoot":"","sources":["../../test/SpannerEmulatorProvisioner.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db-driver-spanner",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"main": "./dist/generated/index.js",
|
|
5
5
|
"types": "./dist/generated/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@google-cloud/spanner": "7.5.0",
|
|
45
|
-
"@proteinjs/db": "^1.
|
|
46
|
-
"@proteinjs/db-query": "^1.6.
|
|
47
|
-
"@proteinjs/db-spanner-common": "^1.3.
|
|
48
|
-
"@proteinjs/db-transaction-context": "^0.5.
|
|
45
|
+
"@proteinjs/db": "^1.30.0",
|
|
46
|
+
"@proteinjs/db-query": "^1.6.3",
|
|
47
|
+
"@proteinjs/db-spanner-common": "^1.3.6",
|
|
48
|
+
"@proteinjs/db-transaction-context": "^0.5.6",
|
|
49
49
|
"@proteinjs/logger": "^1.0.21",
|
|
50
50
|
"@proteinjs/reflection": "^1.1.12",
|
|
51
51
|
"@proteinjs/util": "^1.6.1"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"ts-jest": "29.1.1",
|
|
64
64
|
"typescript": "5.2.2"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "fd3dd48fae93c6da8c0b3decd6dd30a506a9109a"
|
|
67
67
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { SpannerDriver } from '@proteinjs/db-driver-spanner';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getDb,
|
|
4
|
+
getDbAsSystem,
|
|
5
|
+
Migration,
|
|
6
|
+
MigrationRunner,
|
|
7
|
+
MigrationTable,
|
|
8
|
+
SourceRecordRepo,
|
|
9
|
+
Table,
|
|
10
|
+
} from '@proteinjs/db';
|
|
3
11
|
import { registerTestUser, clearTestUser } from '@proteinjs/db/test';
|
|
4
12
|
import { getDropTestTable } from './util/getDropTestTable';
|
|
5
13
|
import { SpannerEmulatorProvisioner } from './util/SpannerEmulatorProvisioner';
|
|
@@ -41,6 +49,43 @@ describe('MigrationRunner (spanner)', () => {
|
|
|
41
49
|
run: async () => 'migration output',
|
|
42
50
|
} as unknown as Migration;
|
|
43
51
|
|
|
52
|
+
// ensureMigrationRun fixtures carry run counters: re-running any of them is VISIBLE as a
|
|
53
|
+
// counter increment, so idempotence and retry are asserted on the migration's own effect,
|
|
54
|
+
// not on bookkeeping interactions.
|
|
55
|
+
let ensureBootRunCount = 0;
|
|
56
|
+
const ensureBootMigration = {
|
|
57
|
+
id: 'migration-runner-test-ensure-boot',
|
|
58
|
+
description: 'runs at boot with no user session',
|
|
59
|
+
run: async () => {
|
|
60
|
+
ensureBootRunCount++;
|
|
61
|
+
return 'boot output';
|
|
62
|
+
},
|
|
63
|
+
} as unknown as Migration;
|
|
64
|
+
|
|
65
|
+
let ensureIdempotentRunCount = 0;
|
|
66
|
+
const ensureIdempotentMigration = {
|
|
67
|
+
// Ids stay <= 36 chars — the migration table's id column is uuid-width.
|
|
68
|
+
id: 'migration-runner-test-ensure-skip',
|
|
69
|
+
description: 'must not re-run once successful',
|
|
70
|
+
run: async () => {
|
|
71
|
+
ensureIdempotentRunCount++;
|
|
72
|
+
return 'idempotent output';
|
|
73
|
+
},
|
|
74
|
+
} as unknown as Migration;
|
|
75
|
+
|
|
76
|
+
let ensureRetryAttempts = 0;
|
|
77
|
+
const ensureRetriedMigration = {
|
|
78
|
+
id: 'migration-runner-test-ensure-retried',
|
|
79
|
+
description: 'fails first, fixed on retry',
|
|
80
|
+
run: async () => {
|
|
81
|
+
ensureRetryAttempts++;
|
|
82
|
+
if (ensureRetryAttempts === 1) {
|
|
83
|
+
throw new Error('ensure migration blew up mid-run');
|
|
84
|
+
}
|
|
85
|
+
return 'fixed on retry';
|
|
86
|
+
},
|
|
87
|
+
} as unknown as Migration;
|
|
88
|
+
|
|
44
89
|
beforeAll(async () => {
|
|
45
90
|
// Explicit admin identity (UserAuth is fail-closed): the migration table's doors ride the
|
|
46
91
|
// 'dev' permission with admin break-glass — the permission mapping itself is pinned in
|
|
@@ -53,7 +98,13 @@ describe('MigrationRunner (spanner)', () => {
|
|
|
53
98
|
});
|
|
54
99
|
await dropTable(migrationTable);
|
|
55
100
|
await spannerDriver.getTableManager().loadTable(migrationTable);
|
|
56
|
-
for (const migration of [
|
|
101
|
+
for (const migration of [
|
|
102
|
+
failingMigration,
|
|
103
|
+
succeedingMigration,
|
|
104
|
+
ensureBootMigration,
|
|
105
|
+
ensureIdempotentMigration,
|
|
106
|
+
ensureRetriedMigration,
|
|
107
|
+
]) {
|
|
57
108
|
sourceRecordRepo.loadSourceRecord(migrationTable.name, migration);
|
|
58
109
|
await getDb().insert(migrationTable, migration);
|
|
59
110
|
}
|
|
@@ -85,4 +136,55 @@ describe('MigrationRunner (spanner)', () => {
|
|
|
85
136
|
expect(row.output).toBe('migration output');
|
|
86
137
|
expect(row.duration).toBeTruthy();
|
|
87
138
|
});
|
|
139
|
+
|
|
140
|
+
describe('ensureMigrationRun (boot path)', () => {
|
|
141
|
+
// Boot context: NO user session exists — UserAuth is fail-closed, so any getDb() bookkeeping
|
|
142
|
+
// would deny. These tests run with the suite's test identity CLEARED to pin that
|
|
143
|
+
// ensureMigrationRun records through the system db. Assertions read as system for the same
|
|
144
|
+
// reason; the identity is restored for the rest of the suite (afterAll drops the table via
|
|
145
|
+
// getDb-adjacent paths).
|
|
146
|
+
beforeEach(() => clearTestUser());
|
|
147
|
+
afterEach(() => registerTestUser());
|
|
148
|
+
|
|
149
|
+
test('runs a pending migration with no user session and records success', async () => {
|
|
150
|
+
const runner = new MigrationRunner();
|
|
151
|
+
await runner.ensureMigrationRun(ensureBootMigration.id);
|
|
152
|
+
|
|
153
|
+
expect(ensureBootRunCount).toBe(1);
|
|
154
|
+
const row = await getDbAsSystem().get(migrationTable, { id: ensureBootMigration.id });
|
|
155
|
+
expect(row.status).toBe('success');
|
|
156
|
+
expect(row.output).toBe('boot output');
|
|
157
|
+
expect(row.duration).toBeTruthy();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test('a second call skips an already-successful migration', async () => {
|
|
161
|
+
const runner = new MigrationRunner();
|
|
162
|
+
await runner.ensureMigrationRun(ensureIdempotentMigration.id);
|
|
163
|
+
expect(ensureIdempotentRunCount).toBe(1);
|
|
164
|
+
|
|
165
|
+
await runner.ensureMigrationRun(ensureIdempotentMigration.id);
|
|
166
|
+
|
|
167
|
+
// The migration's effect did not repeat — 'ensure' skipped the successful row.
|
|
168
|
+
expect(ensureIdempotentRunCount).toBe(1);
|
|
169
|
+
const row = await getDbAsSystem().get(migrationTable, { id: ensureIdempotentMigration.id });
|
|
170
|
+
expect(row.status).toBe('success');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('a throwing migration records failure and is retried by the next call', async () => {
|
|
174
|
+
const runner = new MigrationRunner();
|
|
175
|
+
await runner.ensureMigrationRun(ensureRetriedMigration.id);
|
|
176
|
+
|
|
177
|
+
expect(ensureRetryAttempts).toBe(1);
|
|
178
|
+
const failedRow = await getDbAsSystem().get(migrationTable, { id: ensureRetriedMigration.id });
|
|
179
|
+
expect(failedRow.status).toBe('failure');
|
|
180
|
+
expect(failedRow.failureMessage).toBe('ensure migration blew up mid-run');
|
|
181
|
+
|
|
182
|
+
await runner.ensureMigrationRun(ensureRetriedMigration.id);
|
|
183
|
+
|
|
184
|
+
expect(ensureRetryAttempts).toBe(2);
|
|
185
|
+
const retriedRow = await getDbAsSystem().get(migrationTable, { id: ensureRetriedMigration.id });
|
|
186
|
+
expect(retriedRow.status).toBe('success');
|
|
187
|
+
expect(retriedRow.output).toBe('fixed on retry');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
88
190
|
});
|
|
@@ -120,4 +120,32 @@ describe('Session-shaped record table query (the admin Sessions table path)', ()
|
|
|
120
120
|
const countQb = new QueryBuilderFactory().createQueryBuilder<SessionShape>(table);
|
|
121
121
|
expect(await db.getRowCount(table, countQb)).toBe(3);
|
|
122
122
|
}, 60000);
|
|
123
|
+
|
|
124
|
+
test('a Moment condition value binds as TIMESTAMP (the cursor-window query shape)', async () => {
|
|
125
|
+
// EXACTLY the home recents cursor-window shape: `updated < <Moment>` sorted desc — the
|
|
126
|
+
// condition value is a Moment the way every deserialized row field (and every wire-revived
|
|
127
|
+
// condition) is. Pre-fix this bound the raw Moment into a TIMESTAMP param and Spanner
|
|
128
|
+
// rejected the query (INVALID_ARGUMENT: Expected TIMESTAMP — the 2026-08-13 home-recents
|
|
129
|
+
// skeleton hang). Rows live in their own future time band so the bracketing conditions
|
|
130
|
+
// exclude the other test's rows without coupling to its clock.
|
|
131
|
+
const band = Date.now() + 600_000;
|
|
132
|
+
for (let n = 4; n <= 6; n++) {
|
|
133
|
+
await systemDb.insert(table, storeShapedRow(n, band + (n - 3) * 1000) as any);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const qb = new QueryBuilderFactory()
|
|
137
|
+
.createQueryBuilder<SessionShape>(table)
|
|
138
|
+
.condition({ field: 'updated', operator: '>', value: moment(band + 500) as any })
|
|
139
|
+
.condition({ field: 'updated', operator: '<', value: moment(band + 2500) as any })
|
|
140
|
+
.sort([{ field: 'updated', desc: true }]);
|
|
141
|
+
const rows = await db.query(table, qb);
|
|
142
|
+
expect(rows.map((row) => row.sessionId)).toEqual(['test-session-5', 'test-session-4']);
|
|
143
|
+
|
|
144
|
+
// The IN/array leg binds each element through the same conversion.
|
|
145
|
+
const inQb = new QueryBuilderFactory()
|
|
146
|
+
.createQueryBuilder<SessionShape>(table)
|
|
147
|
+
.condition({ field: 'updated', operator: 'IN', value: [moment(band + 1000), moment(band + 3000)] as any });
|
|
148
|
+
const inRows = await db.query(table, inQb);
|
|
149
|
+
expect(inRows.map((row) => row.sessionId).sort()).toEqual(['test-session-4', 'test-session-6']);
|
|
150
|
+
}, 60000);
|
|
123
151
|
});
|
|
@@ -69,25 +69,71 @@ describe('SpannerEmulatorProvisioner', () => {
|
|
|
69
69
|
|
|
70
70
|
// Model the stranded transaction a killed jest worker leaves behind: a read-write
|
|
71
71
|
// transaction with real uncommitted work, held open across the re-ensure.
|
|
72
|
-
|
|
72
|
+
//
|
|
73
|
+
// The control's premise — OUR transaction is the active one when the DDL arrives — can be
|
|
74
|
+
// invalidated by ambient in-process writers (session-pool maintenance, neighbor suites'
|
|
75
|
+
// detached work): the emulator allows ONE active read-write transaction, so a neighbor's
|
|
76
|
+
// write aborts ours mid-window and the DDL sails through. That is a false alarm, not the
|
|
77
|
+
// invariant breaking (bit twice on CI run 31760208027 once the suite set grew). Each
|
|
78
|
+
// attempt re-establishes the premise; the control only FAILS when DDL succeeds against a
|
|
79
|
+
// transaction verified still active.
|
|
80
|
+
let transaction: Transaction | undefined;
|
|
73
81
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
// Emulator-semantics detection, not a mocked assumption: newer emulator images ALLOW
|
|
83
|
+
// schema changes while a read-write transaction is active (the real service always
|
|
84
|
+
// did), where older images reject with FAILED_PRECONDITION. The stranded-transaction
|
|
85
|
+
// failure class this invariant guards exists exactly where the rejection does — so the
|
|
86
|
+
// control leg doubles as the detector. Probe soundness: after an unexpected DDL
|
|
87
|
+
// success, COMMIT the held transaction — commit of a neighbor-aborted transaction
|
|
88
|
+
// throws ABORTED (retry: the premise was invalidated, not the semantics), commit of a
|
|
89
|
+
// still-active one succeeds (rejection semantics genuinely absent on this emulator).
|
|
90
|
+
// A SELECT probe is unsound here: an aborted handle can silently re-begin and answer.
|
|
91
|
+
let controlHeld = false;
|
|
92
|
+
let rejectionSemanticsAbsent = false;
|
|
93
|
+
for (let attempt = 0; attempt < 3 && !controlHeld && !rejectionSemanticsAbsent; attempt++) {
|
|
94
|
+
if (transaction) {
|
|
95
|
+
await transaction.rollback().catch(() => undefined);
|
|
96
|
+
transaction.end();
|
|
97
|
+
}
|
|
98
|
+
[transaction] = (await database.getTransaction()) as unknown as [Transaction];
|
|
99
|
+
await transaction.runUpdate({
|
|
100
|
+
sql: `INSERT INTO ${SCRATCH_TABLE} (id) VALUES (@id)`,
|
|
101
|
+
params: { id: `re-ensure-${attempt}-${Date.now()}` },
|
|
102
|
+
});
|
|
78
103
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// the invariant leg below has lost its bite.
|
|
83
|
-
await expect(
|
|
84
|
-
(async () => {
|
|
104
|
+
// Control leg: on rejecting emulators the open transaction refuses DDL in this
|
|
105
|
+
// window — the same rejection the pre-fix unconditional pin ALTER died on.
|
|
106
|
+
try {
|
|
85
107
|
const [op] = await database.updateSchema(
|
|
86
108
|
`ALTER DATABASE \`${spannerConfig.databaseName}\` SET OPTIONS (version_retention_period = '1m')`
|
|
87
109
|
);
|
|
88
110
|
await op.promise();
|
|
89
|
-
}
|
|
90
|
-
|
|
111
|
+
} catch (error) {
|
|
112
|
+
expect(error).toMatchObject({ code: 9 /* gRPC FAILED_PRECONDITION */ });
|
|
113
|
+
controlHeld = true;
|
|
114
|
+
}
|
|
115
|
+
if (controlHeld) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
rejectionSemanticsAbsent = await transaction.commit().then(
|
|
120
|
+
() => true,
|
|
121
|
+
() => false
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (rejectionSemanticsAbsent) {
|
|
126
|
+
// DDL succeeded against a verifiably-active transaction: this emulator does not
|
|
127
|
+
// reject schema changes under open transactions, so a stranded transaction cannot
|
|
128
|
+
// fail a re-ensure here — the invariant's failure class is absent by construction.
|
|
129
|
+
// The reconcile leg below still bites: re-ensure resolves and retention stays pinned.
|
|
130
|
+
transaction?.end();
|
|
131
|
+
transaction = undefined;
|
|
132
|
+
} else if (!controlHeld) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
'control could not hold an active transaction across 3 attempts — ambient writers kept aborting it'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
91
137
|
|
|
92
138
|
// The invariant: re-ensure resolves inside the same window — it issued zero DDL.
|
|
93
139
|
await expect(SpannerEmulatorProvisioner.ensureProvisioned(spannerConfig)).resolves.toBeUndefined();
|
|
@@ -98,8 +144,10 @@ describe('SpannerEmulatorProvisioner', () => {
|
|
|
98
144
|
} finally {
|
|
99
145
|
// Never strand the transaction on the shared test emulator — that would poison later
|
|
100
146
|
// suites' DDL exactly like the class this test guards against.
|
|
101
|
-
|
|
102
|
-
|
|
147
|
+
if (transaction) {
|
|
148
|
+
await transaction.rollback().catch(() => undefined);
|
|
149
|
+
transaction.end();
|
|
150
|
+
}
|
|
103
151
|
}
|
|
104
152
|
}, 60_000);
|
|
105
153
|
});
|