@voltro/testing 0.70.0 → 0.71.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 +1289 -111
- package/dist/dialect.js +642 -363
- package/dist/index.js +171 -158
- package/package.json +8 -8
package/dist/dialect.js
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import { _voltroMigrationPlansTable as e, array as t, boolean as n, bulkInsertLimitsFor as r, dropped as i, eq as a, id as o, integer as s, json as c, registerTable as l,
|
|
2
|
-
import { Effect as
|
|
3
|
-
import { _voltroUndoLogTable as
|
|
4
|
-
import { afterEach as
|
|
5
|
-
import { SqlClient as
|
|
6
|
-
import { applyPlan as
|
|
1
|
+
import { _voltroMigrationPlansTable as e, array as t, boolean as n, bulkInsertLimitsFor as r, dropped as i, eq as a, id as o, integer as s, json as c, registerTable as l, registerWriteRecorder as u, runWithWriteAttribution as d, table as f, text as p, timestamp as m, unregisterWriteRecorder as h } from "@voltro/database";
|
|
2
|
+
import { Effect as g } from "effect";
|
|
3
|
+
import { _voltroUndoLogTable as _, applyInverses as v, makeSchemaRegistry as y, synthesizeInverse as b, wrapStoreWithMixinBehaviour as x } from "@voltro/runtime";
|
|
4
|
+
import { afterEach as S, beforeEach as C, describe as w, expect as T, it as E } from "vitest";
|
|
5
|
+
import { SqlClient as D } from "@effect/sql";
|
|
6
|
+
import { applyPlan as O, applySchema as k, introspectSchema as A, planMigrations as j } from "@voltro/database/sql";
|
|
7
7
|
//#region src/dialectParity.ts
|
|
8
|
-
var
|
|
8
|
+
var M = (e) => ({
|
|
9
9
|
table: e,
|
|
10
10
|
predicate: void 0,
|
|
11
11
|
order: [],
|
|
12
12
|
take: void 0,
|
|
13
13
|
skip: void 0,
|
|
14
14
|
projection: void 0
|
|
15
|
-
}),
|
|
15
|
+
}), N = f("voltro_parity_a", {
|
|
16
16
|
id: o(),
|
|
17
|
-
title:
|
|
17
|
+
title: p(),
|
|
18
18
|
count: s()
|
|
19
|
-
}),
|
|
19
|
+
}), P = f("voltro_parity_b", {
|
|
20
20
|
id: o(),
|
|
21
|
-
title:
|
|
22
|
-
}),
|
|
21
|
+
title: p()
|
|
22
|
+
}), F = f("voltro_parity_c", {
|
|
23
23
|
id: o(),
|
|
24
|
-
title:
|
|
24
|
+
title: p(),
|
|
25
25
|
count: s(),
|
|
26
26
|
flag: n().default(!1)
|
|
27
|
-
}),
|
|
27
|
+
}), I = f("voltro_parity_d", {
|
|
28
28
|
id: o(),
|
|
29
|
-
title:
|
|
30
|
-
at:
|
|
29
|
+
title: p(),
|
|
30
|
+
at: m().nullable().onUpdate("now"),
|
|
31
31
|
n: s().nullable()
|
|
32
|
-
}),
|
|
32
|
+
}), L = f("voltro_parity_upsert", {
|
|
33
33
|
id: o(),
|
|
34
|
-
teamId:
|
|
34
|
+
teamId: p(),
|
|
35
35
|
year: s(),
|
|
36
36
|
hours: s().nullable()
|
|
37
|
-
}).unique("byTeamYear", ["teamId", "year"]),
|
|
37
|
+
}).unique("byTeamYear", ["teamId", "year"]), R = f("voltro_parity_values", {
|
|
38
38
|
id: o(),
|
|
39
|
-
title:
|
|
39
|
+
title: p().onUpdate(() => "automatic"),
|
|
40
40
|
metadata: c().nullable(),
|
|
41
|
-
labels: t(
|
|
41
|
+
labels: t(p()).nullable()
|
|
42
42
|
});
|
|
43
|
-
l(
|
|
44
|
-
var
|
|
43
|
+
l(R);
|
|
44
|
+
var z = f("voltro_parity_json_names", {
|
|
45
45
|
id: o(),
|
|
46
46
|
key: c(),
|
|
47
47
|
value: c()
|
|
48
48
|
});
|
|
49
|
-
l(
|
|
50
|
-
var
|
|
51
|
-
userId:
|
|
52
|
-
orgId:
|
|
53
|
-
role:
|
|
54
|
-
}).primaryKey(["userId", "orgId"]),
|
|
49
|
+
l(z);
|
|
50
|
+
var B = f("voltro_parity_composite", {
|
|
51
|
+
userId: p(),
|
|
52
|
+
orgId: p(),
|
|
53
|
+
role: p()
|
|
54
|
+
}).primaryKey(["userId", "orgId"]), V = f("voltro_parity_bulk", {
|
|
55
55
|
id: o(),
|
|
56
|
-
title:
|
|
56
|
+
title: p(),
|
|
57
57
|
count: s(),
|
|
58
58
|
flag: n().default(!1)
|
|
59
|
-
}),
|
|
59
|
+
}), H = (e) => {
|
|
60
60
|
let t = r(e);
|
|
61
61
|
if (t === void 0) return 0;
|
|
62
|
-
let n = Object.keys(
|
|
62
|
+
let n = Object.keys(V.fields).length;
|
|
63
63
|
return Math.max(1, Math.min(Math.floor(t.maxBindParameters / n), t.maxRowsPerStatement ?? Infinity));
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
let
|
|
67
|
-
|
|
68
|
-
await
|
|
69
|
-
A,
|
|
70
|
-
j,
|
|
71
|
-
M,
|
|
64
|
+
}, U = (n) => {
|
|
65
|
+
w(`dialect parity: ${n.name ?? n.dialect.id}`, () => {
|
|
66
|
+
let r = null;
|
|
67
|
+
C(async () => {
|
|
68
|
+
await n.setup(), r = await n.make(), await r.migrate([
|
|
72
69
|
N,
|
|
73
|
-
R,
|
|
74
70
|
P,
|
|
75
|
-
L,
|
|
76
71
|
F,
|
|
77
72
|
I,
|
|
78
|
-
|
|
73
|
+
V,
|
|
74
|
+
L,
|
|
75
|
+
B,
|
|
76
|
+
R,
|
|
77
|
+
z,
|
|
78
|
+
_
|
|
79
79
|
]);
|
|
80
80
|
for (let e of [
|
|
81
81
|
"voltro_parity_a",
|
|
@@ -87,270 +87,270 @@ var L = u("voltro_parity_composite", {
|
|
|
87
87
|
"voltro_parity_json_names",
|
|
88
88
|
"_voltro_undo_log"
|
|
89
89
|
]) {
|
|
90
|
-
let t = await
|
|
91
|
-
for (let
|
|
90
|
+
let t = await r.store.query(M(e));
|
|
91
|
+
for (let n of t) await r.store.delete(e, n.id);
|
|
92
92
|
}
|
|
93
|
-
for (let e of await
|
|
93
|
+
for (let e of await r.store.query(M("voltro_parity_composite"))) await r.store.delete("voltro_parity_composite", {
|
|
94
94
|
userId: e.userId,
|
|
95
95
|
orgId: e.orgId
|
|
96
96
|
});
|
|
97
|
-
}),
|
|
98
|
-
await
|
|
99
|
-
}),
|
|
100
|
-
let
|
|
101
|
-
await a(
|
|
102
|
-
let e = yield*
|
|
103
|
-
for (let
|
|
97
|
+
}), S(async () => {
|
|
98
|
+
await r?.dispose(), await n.teardown(), r = null;
|
|
99
|
+
}), E("rename-table: the rows move, and the plan converges", async () => {
|
|
100
|
+
let t = "voltro_rename_src", i = "_voltro_rename_dst", a = r.run, s = async () => {
|
|
101
|
+
await a(g.gen(function* () {
|
|
102
|
+
let e = yield* D.SqlClient;
|
|
103
|
+
for (let n of [i, t]) yield* g.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${n}`), () => []);
|
|
104
104
|
})).catch(() => {});
|
|
105
|
-
}, c = () => a(
|
|
106
|
-
let e = yield*
|
|
105
|
+
}, c = () => a(g.gen(function* () {
|
|
106
|
+
let e = yield* D.SqlClient, n = yield* A(e);
|
|
107
107
|
return {
|
|
108
|
-
...
|
|
109
|
-
tables:
|
|
108
|
+
...n,
|
|
109
|
+
tables: n.tables.filter((e) => e.name === t || e.name === i)
|
|
110
110
|
};
|
|
111
|
-
})), l = async () =>
|
|
112
|
-
declared: [
|
|
111
|
+
})), l = async () => j({
|
|
112
|
+
declared: [d],
|
|
113
113
|
live: await c(),
|
|
114
|
-
dialect:
|
|
114
|
+
dialect: n.dialect.id
|
|
115
115
|
});
|
|
116
116
|
await s();
|
|
117
|
-
let
|
|
117
|
+
let u = f(t, {
|
|
118
118
|
id: o({ prefix: "rn" }),
|
|
119
|
-
title:
|
|
120
|
-
}),
|
|
119
|
+
title: p()
|
|
120
|
+
}), d = f(i, {
|
|
121
121
|
id: o({ prefix: "rn" }),
|
|
122
|
-
title:
|
|
123
|
-
}).renamedFrom(
|
|
124
|
-
await
|
|
122
|
+
title: p()
|
|
123
|
+
}).renamedFrom(t);
|
|
124
|
+
await r.migrate([u, e]), await r.store.insert(t, {
|
|
125
125
|
id: "rn_1",
|
|
126
126
|
title: "one"
|
|
127
|
-
}), await
|
|
127
|
+
}), await r.store.insert(t, {
|
|
128
128
|
id: "rn_2",
|
|
129
129
|
title: "two"
|
|
130
130
|
});
|
|
131
|
-
let
|
|
132
|
-
|
|
133
|
-
let e = yield*
|
|
134
|
-
return yield*
|
|
135
|
-
declared: [
|
|
131
|
+
let m = await l();
|
|
132
|
+
T(m.operations.some((e) => e.op.kind === "rename-table"), "the marker must fold").toBe(!0), T(m.operations.filter((e) => e.blocked !== void 0), "nothing may be blocked").toEqual([]), await a(g.gen(function* () {
|
|
133
|
+
let e = yield* D.SqlClient;
|
|
134
|
+
return yield* O(e, m, {
|
|
135
|
+
declared: [d],
|
|
136
136
|
appliedBy: "dialect-parity",
|
|
137
137
|
environment: "dev",
|
|
138
138
|
source: "auto-diff",
|
|
139
|
-
replan: () =>
|
|
139
|
+
replan: () => g.promise(l)
|
|
140
140
|
});
|
|
141
141
|
}));
|
|
142
|
-
let
|
|
143
|
-
|
|
144
|
-
}),
|
|
145
|
-
let
|
|
146
|
-
await a(
|
|
147
|
-
let e = yield*
|
|
148
|
-
yield*
|
|
142
|
+
let h = await r.store.query(M(i));
|
|
143
|
+
T(h.map((e) => e.id).sort(), "every row survived the rename").toEqual(["rn_1", "rn_2"]), T((await l()).operations, `${n.dialect.id}: the plan must converge`).toEqual([]), await s();
|
|
144
|
+
}), E("schema evolution: create → add column → add index → drop column, converging at each step", async () => {
|
|
145
|
+
let t = "voltro_parity_evolve", a = r.run, c = async () => {
|
|
146
|
+
await a(g.gen(function* () {
|
|
147
|
+
let e = yield* D.SqlClient;
|
|
148
|
+
yield* g.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${t}`), () => []);
|
|
149
149
|
})).catch(() => {});
|
|
150
|
-
}, l = () => a(
|
|
151
|
-
let e = yield*
|
|
150
|
+
}, l = () => a(g.gen(function* () {
|
|
151
|
+
let e = yield* D.SqlClient, n = yield* A(e);
|
|
152
152
|
return {
|
|
153
|
-
...
|
|
154
|
-
tables:
|
|
153
|
+
...n,
|
|
154
|
+
tables: n.tables.filter((e) => e.name === t)
|
|
155
155
|
};
|
|
156
|
-
})),
|
|
156
|
+
})), u = async (e) => j({
|
|
157
157
|
declared: e,
|
|
158
158
|
live: await l(),
|
|
159
|
-
dialect:
|
|
160
|
-
}),
|
|
161
|
-
let i = await
|
|
162
|
-
|
|
163
|
-
let
|
|
164
|
-
return yield*
|
|
159
|
+
dialect: n.dialect.id
|
|
160
|
+
}), d = async () => (await l()).tables[0]?.columns.map((e) => e.name) ?? [], m = async (e, r) => {
|
|
161
|
+
let i = await u(e);
|
|
162
|
+
r && T(i.operations.length, `${n.dialect.id}: nothing to apply — the step asserts nothing`).toBeGreaterThan(0), T(i.operations.filter((e) => e.blocked !== void 0), `${n.dialect.id}: nothing may be blocked`).toEqual([]), i.operations.length !== 0 && (await a(g.gen(function* () {
|
|
163
|
+
let r = yield* D.SqlClient;
|
|
164
|
+
return yield* O(r, i, {
|
|
165
165
|
declared: e,
|
|
166
166
|
appliedBy: "dialect-parity",
|
|
167
167
|
environment: "dev",
|
|
168
168
|
source: "auto-diff",
|
|
169
|
-
replan: (
|
|
169
|
+
replan: (r) => g.map(A(r), (r) => j({
|
|
170
170
|
declared: e,
|
|
171
171
|
live: {
|
|
172
|
-
...
|
|
173
|
-
tables:
|
|
172
|
+
...r,
|
|
173
|
+
tables: r.tables.filter((e) => e.name === t)
|
|
174
174
|
},
|
|
175
|
-
dialect:
|
|
175
|
+
dialect: n.dialect.id
|
|
176
176
|
}))
|
|
177
177
|
});
|
|
178
|
-
})),
|
|
178
|
+
})), T((await u(e)).operations, `${n.dialect.id}: the plan must converge`).toEqual([]));
|
|
179
179
|
};
|
|
180
|
-
await c(), await
|
|
180
|
+
await c(), await r.migrate([e]), await m([f(t, {
|
|
181
181
|
id: o({ prefix: "ev" }),
|
|
182
|
-
name:
|
|
183
|
-
})], !0),
|
|
182
|
+
name: p()
|
|
183
|
+
})], !0), T(await d(), `${n.dialect.id}: v1 columns`).toEqual(["id", "name"]), await r.store.insert(t, {
|
|
184
184
|
id: "ev_1",
|
|
185
185
|
name: "one"
|
|
186
|
-
}), await
|
|
186
|
+
}), await r.store.insert(t, {
|
|
187
187
|
id: "ev_2",
|
|
188
188
|
name: "two"
|
|
189
|
-
}), await
|
|
189
|
+
}), await m([f(t, {
|
|
190
190
|
id: o({ prefix: "ev" }),
|
|
191
|
-
name:
|
|
191
|
+
name: p(),
|
|
192
192
|
qty: s().nullable()
|
|
193
|
-
})], !0),
|
|
193
|
+
})], !0), T(await d()).toContain("qty"), await m([f(t, {
|
|
194
194
|
id: o({ prefix: "ev" }),
|
|
195
|
-
name:
|
|
195
|
+
name: p(),
|
|
196
196
|
qty: s().nullable()
|
|
197
|
-
}).index(["qty"])], !0), await
|
|
197
|
+
}).index(["qty"])], !0), await m([f(t, {
|
|
198
198
|
id: o({ prefix: "ev" }),
|
|
199
|
-
name:
|
|
199
|
+
name: p(),
|
|
200
200
|
qty: i()
|
|
201
|
-
})], !0),
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
}),
|
|
205
|
-
let
|
|
206
|
-
await i(
|
|
207
|
-
let e = yield*
|
|
208
|
-
yield*
|
|
201
|
+
})], !0), T(await d(), `${n.dialect.id}: v4 columns`).toEqual(["id", "name"]);
|
|
202
|
+
let h = await r.store.query(M(t));
|
|
203
|
+
T(h.map((e) => e.id).sort(), `${n.dialect.id}: rows survived the evolution`).toEqual(["ev_1", "ev_2"]), await c();
|
|
204
|
+
}), E("re-applying a create-table plan is a no-op, indexes included", async () => {
|
|
205
|
+
let t = "voltro_parity_reapply", i = r.run, a = async () => {
|
|
206
|
+
await i(g.gen(function* () {
|
|
207
|
+
let e = yield* D.SqlClient;
|
|
208
|
+
yield* g.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${t}`), () => []);
|
|
209
209
|
})).catch(() => {});
|
|
210
|
-
}, c = () => i(
|
|
211
|
-
let e = yield*
|
|
210
|
+
}, c = () => i(g.gen(function* () {
|
|
211
|
+
let e = yield* D.SqlClient, n = yield* A(e);
|
|
212
212
|
return {
|
|
213
|
-
...
|
|
214
|
-
tables:
|
|
213
|
+
...n,
|
|
214
|
+
tables: n.tables.filter((e) => e.name === t)
|
|
215
215
|
};
|
|
216
|
-
})), l =
|
|
216
|
+
})), l = f(t, {
|
|
217
217
|
id: o({ prefix: "ra" }),
|
|
218
|
-
name:
|
|
218
|
+
name: p(),
|
|
219
219
|
qty: s().nullable()
|
|
220
|
-
}).index(["qty"]),
|
|
220
|
+
}).index(["qty"]), u = async () => j({
|
|
221
221
|
declared: [l],
|
|
222
222
|
live: await c(),
|
|
223
|
-
dialect:
|
|
223
|
+
dialect: n.dialect.id
|
|
224
224
|
});
|
|
225
|
-
await a(), await
|
|
226
|
-
let
|
|
227
|
-
|
|
228
|
-
for (let e = 0; e < 2; e++) await i(
|
|
229
|
-
let e = yield*
|
|
230
|
-
return yield*
|
|
225
|
+
await a(), await r.migrate([e]);
|
|
226
|
+
let d = await u();
|
|
227
|
+
T(d.operations.some((e) => e.op.kind === "create-table"), `${n.dialect.id}: expected a create-table to re-apply`).toBe(!0);
|
|
228
|
+
for (let e = 0; e < 2; e++) await i(g.gen(function* () {
|
|
229
|
+
let e = yield* D.SqlClient;
|
|
230
|
+
return yield* O(e, d, {
|
|
231
231
|
declared: [l],
|
|
232
232
|
appliedBy: "dialect-parity",
|
|
233
233
|
environment: "dev",
|
|
234
234
|
source: "auto-diff",
|
|
235
|
-
replan: (e) =>
|
|
235
|
+
replan: (e) => g.map(A(e), (e) => j({
|
|
236
236
|
declared: [l],
|
|
237
237
|
live: {
|
|
238
238
|
...e,
|
|
239
|
-
tables: e.tables.filter((e) => e.name ===
|
|
239
|
+
tables: e.tables.filter((e) => e.name === t)
|
|
240
240
|
},
|
|
241
|
-
dialect:
|
|
241
|
+
dialect: n.dialect.id
|
|
242
242
|
}))
|
|
243
243
|
});
|
|
244
244
|
}));
|
|
245
|
-
|
|
245
|
+
T((await u()).operations, `${n.dialect.id}: the re-applied plan must converge`).toEqual([]), await a();
|
|
246
246
|
});
|
|
247
|
-
let
|
|
248
|
-
let e = yield*
|
|
249
|
-
yield*
|
|
250
|
-
})),
|
|
247
|
+
let l = "voltro_parity_bulk", m = () => r.run(g.gen(function* () {
|
|
248
|
+
let e = yield* D.SqlClient;
|
|
249
|
+
yield* g.orElseSucceed(e.unsafe(`DELETE FROM ${l}`), () => []);
|
|
250
|
+
})), k = (e, t) => Array.from({ length: e }, (e, n) => ({
|
|
251
251
|
id: `${t}_${String(n).padStart(6, "0")}`,
|
|
252
252
|
title: `row ${n}`,
|
|
253
253
|
count: n,
|
|
254
254
|
flag: !1
|
|
255
255
|
}));
|
|
256
|
-
|
|
257
|
-
let e =
|
|
258
|
-
|
|
259
|
-
let
|
|
260
|
-
await
|
|
261
|
-
let
|
|
262
|
-
|
|
263
|
-
let
|
|
264
|
-
|
|
265
|
-
}, 12e4),
|
|
266
|
-
let e =
|
|
267
|
-
await
|
|
268
|
-
let
|
|
269
|
-
|
|
270
|
-
...
|
|
271
|
-
id:
|
|
272
|
-
}, await
|
|
273
|
-
let
|
|
274
|
-
|
|
275
|
-
}, 12e4),
|
|
276
|
-
await
|
|
277
|
-
}),
|
|
278
|
-
let e = await
|
|
256
|
+
E("insertMany past this dialect's one-statement ceiling lands EVERY row", async () => {
|
|
257
|
+
let e = H(n.dialect.id);
|
|
258
|
+
T(e, `no bulk-insert limits are declared for '${n.dialect.id}' — a dialect with no entry chunks at nothing, so this scenario would pass having tested nothing`).toBeGreaterThan(0);
|
|
259
|
+
let t = e + 5;
|
|
260
|
+
await m();
|
|
261
|
+
let i = await r.store.insertMany(l, k(t, "bulk"));
|
|
262
|
+
T(i.length, `insertMany returned ${i.length} of ${t} post-images`).toBe(t), T(i[0]?.id).toBe("bulk_000000"), T(i[t - 1]?.id).toBe(`bulk_${String(t - 1).padStart(6, "0")}`);
|
|
263
|
+
let a = await r.store.query(M(l));
|
|
264
|
+
T(a.length, "the database holds fewer rows than insertMany reported").toBe(t), await m();
|
|
265
|
+
}, 12e4), E("a chunked insertMany is still ALL-OR-NOTHING", async () => {
|
|
266
|
+
let e = H(n.dialect.id) + 5;
|
|
267
|
+
await m();
|
|
268
|
+
let t = k(e, "atomic");
|
|
269
|
+
t[e - 1] = {
|
|
270
|
+
...t[e - 1],
|
|
271
|
+
id: t[0].id
|
|
272
|
+
}, await T(r.store.insertMany(l, t), "a duplicate primary key must still reject the whole call").rejects.toThrow();
|
|
273
|
+
let i = await r.store.query(M(l));
|
|
274
|
+
T(i.length, `${n.dialect.id}: ${i.length} rows survived a FAILED insertMany — chunking introduced partial success. The chunks must run inside one transaction when the caller holds none.`).toBe(0), await m();
|
|
275
|
+
}, 12e4), E("DDL is idempotent — re-applying the schema is a no-op", async () => {
|
|
276
|
+
await r.migrate([N, P]);
|
|
277
|
+
}), E("insert + query round-trips a row", async () => {
|
|
278
|
+
let e = await r.store.insert("voltro_parity_a", {
|
|
279
279
|
id: "a1",
|
|
280
280
|
title: "one",
|
|
281
281
|
count: 1
|
|
282
282
|
});
|
|
283
|
-
|
|
283
|
+
T(e).toMatchObject({
|
|
284
284
|
id: "a1",
|
|
285
285
|
title: "one",
|
|
286
286
|
count: 1
|
|
287
287
|
});
|
|
288
|
-
let t = await
|
|
289
|
-
|
|
290
|
-
}),
|
|
291
|
-
await
|
|
288
|
+
let t = await r.store.query(M("voltro_parity_a"));
|
|
289
|
+
T(t.length).toBe(1), T(t[0]?.id).toBe("a1");
|
|
290
|
+
}), E("update returns the post-image", async () => {
|
|
291
|
+
await r.store.insert("voltro_parity_a", {
|
|
292
292
|
id: "a2",
|
|
293
293
|
title: "pre",
|
|
294
294
|
count: 1
|
|
295
295
|
});
|
|
296
|
-
let e = await
|
|
297
|
-
|
|
296
|
+
let e = await r.store.update("voltro_parity_a", "a2", { title: "post" });
|
|
297
|
+
T(e).toMatchObject({
|
|
298
298
|
id: "a2",
|
|
299
299
|
title: "post"
|
|
300
300
|
});
|
|
301
|
-
}),
|
|
302
|
-
await
|
|
301
|
+
}), E("delete removes the row + reports true", async () => {
|
|
302
|
+
await r.store.insert("voltro_parity_a", {
|
|
303
303
|
id: "a3",
|
|
304
304
|
title: "x",
|
|
305
305
|
count: 1
|
|
306
|
-
}),
|
|
307
|
-
let e = await
|
|
308
|
-
|
|
309
|
-
}),
|
|
310
|
-
await
|
|
306
|
+
}), T(await r.store.delete("voltro_parity_a", "a3")).toBe(!0);
|
|
307
|
+
let e = await r.store.query(M("voltro_parity_a"));
|
|
308
|
+
T(e.length).toBe(0);
|
|
309
|
+
}), E("a composite key addresses the row, and only that row", async () => {
|
|
310
|
+
await r.store.insert("voltro_parity_composite", {
|
|
311
311
|
userId: "u1",
|
|
312
312
|
orgId: "o1",
|
|
313
313
|
role: "admin"
|
|
314
|
-
}), await
|
|
314
|
+
}), await r.store.insert("voltro_parity_composite", {
|
|
315
315
|
userId: "u1",
|
|
316
316
|
orgId: "o2",
|
|
317
317
|
role: "viewer"
|
|
318
318
|
});
|
|
319
|
-
let e = await
|
|
319
|
+
let e = await r.store.update("voltro_parity_composite", {
|
|
320
320
|
userId: "u1",
|
|
321
321
|
orgId: "o2"
|
|
322
322
|
}, { role: "member" });
|
|
323
|
-
|
|
323
|
+
T(e).toMatchObject({
|
|
324
324
|
userId: "u1",
|
|
325
325
|
orgId: "o2",
|
|
326
326
|
role: "member"
|
|
327
327
|
});
|
|
328
|
-
let t = await
|
|
329
|
-
|
|
330
|
-
}),
|
|
331
|
-
|
|
328
|
+
let t = await r.store.query(M("voltro_parity_composite")), n = new Map(t.map((e) => [e.orgId, e.role]));
|
|
329
|
+
T(n.get("o1")).toBe("admin"), T(n.get("o2")).toBe("member");
|
|
330
|
+
}), E("a composite key that matches nothing is `null` / `false`, not an error", async () => {
|
|
331
|
+
T(await r.store.update("voltro_parity_composite", {
|
|
332
332
|
userId: "nobody",
|
|
333
333
|
orgId: "nowhere"
|
|
334
|
-
}, { role: "x" })).toBeNull(),
|
|
334
|
+
}, { role: "x" })).toBeNull(), T(await r.store.delete("voltro_parity_composite", {
|
|
335
335
|
userId: "nobody",
|
|
336
336
|
orgId: "nowhere"
|
|
337
337
|
})).toBe(!1);
|
|
338
|
-
}),
|
|
339
|
-
await
|
|
338
|
+
}), E("a composite key deletes exactly the row it names", async () => {
|
|
339
|
+
await r.store.insert("voltro_parity_composite", {
|
|
340
340
|
userId: "u2",
|
|
341
341
|
orgId: "o1",
|
|
342
342
|
role: "admin"
|
|
343
|
-
}), await
|
|
343
|
+
}), await r.store.insert("voltro_parity_composite", {
|
|
344
344
|
userId: "u2",
|
|
345
345
|
orgId: "o2",
|
|
346
346
|
role: "viewer"
|
|
347
|
-
}),
|
|
347
|
+
}), T(await r.store.delete("voltro_parity_composite", {
|
|
348
348
|
userId: "u2",
|
|
349
349
|
orgId: "o1"
|
|
350
350
|
})).toBe(!0);
|
|
351
|
-
let e = (await
|
|
352
|
-
|
|
353
|
-
}),
|
|
351
|
+
let e = (await r.store.query(M("voltro_parity_composite"))).filter((e) => e.userId === "u2").map((e) => e.orgId);
|
|
352
|
+
T(e).toEqual(["o2"]);
|
|
353
|
+
}), E("undo: the _voltro_undo_log TEXT changes column round-trips a JSON ChangeSet", async () => {
|
|
354
354
|
let e = [{
|
|
355
355
|
table: "voltro_parity_c",
|
|
356
356
|
op: "update",
|
|
@@ -368,7 +368,7 @@ var L = u("voltro_parity_composite", {
|
|
|
368
368
|
flag: !1
|
|
369
369
|
}
|
|
370
370
|
}];
|
|
371
|
-
await
|
|
371
|
+
await r.store.insert("_voltro_undo_log", {
|
|
372
372
|
id: "undo_p1",
|
|
373
373
|
tag: "parityC.update",
|
|
374
374
|
label: null,
|
|
@@ -380,16 +380,16 @@ var L = u("voltro_parity_composite", {
|
|
|
380
380
|
undone: !1,
|
|
381
381
|
createdAt: /* @__PURE__ */ new Date()
|
|
382
382
|
});
|
|
383
|
-
let t = await
|
|
384
|
-
|
|
385
|
-
}),
|
|
383
|
+
let t = await r.store.query(M("_voltro_undo_log"));
|
|
384
|
+
T(t.length).toBe(1), T(JSON.parse(t[0].changes)).toEqual(e);
|
|
385
|
+
}), E("undo: reverts an INSERT (inverse delete) on this dialect", async () => {
|
|
386
386
|
let e = {
|
|
387
387
|
invocationId: "inv1",
|
|
388
388
|
subject: null,
|
|
389
389
|
changes: [{
|
|
390
390
|
table: "voltro_parity_c",
|
|
391
391
|
op: "insert",
|
|
392
|
-
next: await
|
|
392
|
+
next: await r.store.insert("voltro_parity_c", {
|
|
393
393
|
id: "c2",
|
|
394
394
|
title: "new",
|
|
395
395
|
count: 1,
|
|
@@ -397,25 +397,25 @@ var L = u("voltro_parity_composite", {
|
|
|
397
397
|
})
|
|
398
398
|
}]
|
|
399
399
|
};
|
|
400
|
-
await
|
|
401
|
-
let t = await
|
|
402
|
-
|
|
403
|
-
}),
|
|
404
|
-
await
|
|
400
|
+
await v(r.store, b(e));
|
|
401
|
+
let t = await r.store.query(M("voltro_parity_c"));
|
|
402
|
+
T(t.find((e) => e.id === "c2")).toBeUndefined();
|
|
403
|
+
}), E("undo: reverts an UPDATE including the BOOLEAN, restoring the captured value FAITHFULLY", async () => {
|
|
404
|
+
await r.store.insert("voltro_parity_c", {
|
|
405
405
|
id: "c3",
|
|
406
406
|
title: "t",
|
|
407
407
|
count: 1,
|
|
408
408
|
flag: !0
|
|
409
409
|
});
|
|
410
|
-
let e = (await
|
|
411
|
-
...
|
|
410
|
+
let e = (await r.store.query({
|
|
411
|
+
...M("voltro_parity_c"),
|
|
412
412
|
predicate: a("id", "c3")
|
|
413
|
-
}))[0], t = await
|
|
413
|
+
}))[0], t = await r.store.update("voltro_parity_c", "c3", {
|
|
414
414
|
count: 2,
|
|
415
415
|
flag: !1
|
|
416
416
|
});
|
|
417
|
-
|
|
418
|
-
let
|
|
417
|
+
T(t.flag).not.toEqual(e.flag);
|
|
418
|
+
let n = {
|
|
419
419
|
invocationId: "inv2",
|
|
420
420
|
subject: null,
|
|
421
421
|
changes: [{
|
|
@@ -426,20 +426,20 @@ var L = u("voltro_parity_composite", {
|
|
|
426
426
|
next: t
|
|
427
427
|
}]
|
|
428
428
|
};
|
|
429
|
-
await
|
|
430
|
-
let i = (await
|
|
431
|
-
...
|
|
429
|
+
await v(r.store, b(n));
|
|
430
|
+
let i = (await r.store.query({
|
|
431
|
+
...M("voltro_parity_c"),
|
|
432
432
|
predicate: a("id", "c3")
|
|
433
433
|
}))[0];
|
|
434
|
-
|
|
435
|
-
}),
|
|
436
|
-
let e = await
|
|
434
|
+
T(i.flag).toEqual(e.flag), T(i.count).toEqual(e.count);
|
|
435
|
+
}), E("undo: reverts a DELETE (inverse re-insert) on this dialect", async () => {
|
|
436
|
+
let e = await r.store.insert("voltro_parity_c", {
|
|
437
437
|
id: "c4",
|
|
438
438
|
title: "gone",
|
|
439
439
|
count: 9,
|
|
440
440
|
flag: !0
|
|
441
441
|
});
|
|
442
|
-
await
|
|
442
|
+
await r.store.delete("voltro_parity_c", "c4");
|
|
443
443
|
let t = {
|
|
444
444
|
invocationId: "inv3",
|
|
445
445
|
subject: null,
|
|
@@ -450,46 +450,46 @@ var L = u("voltro_parity_composite", {
|
|
|
450
450
|
prev: e
|
|
451
451
|
}]
|
|
452
452
|
};
|
|
453
|
-
await
|
|
454
|
-
let
|
|
455
|
-
...
|
|
453
|
+
await v(r.store, b(t));
|
|
454
|
+
let n = (await r.store.query({
|
|
455
|
+
...M("voltro_parity_c"),
|
|
456
456
|
predicate: a("id", "c4")
|
|
457
457
|
}))[0];
|
|
458
|
-
|
|
459
|
-
}),
|
|
460
|
-
let e = [], t =
|
|
461
|
-
await
|
|
458
|
+
T(n.id).toBe("c4"), T(n.title).toBe("gone"), T(n.flag).toEqual(e.flag);
|
|
459
|
+
}), E("onChange fires insert + update + delete events", async () => {
|
|
460
|
+
let e = [], t = r.store.onChange((t) => e.push(t.op));
|
|
461
|
+
await r.store.insert("voltro_parity_a", {
|
|
462
462
|
id: "a4",
|
|
463
463
|
title: "a",
|
|
464
464
|
count: 1
|
|
465
|
-
}), await
|
|
465
|
+
}), await r.store.update("voltro_parity_a", "a4", { title: "b" }), await r.store.delete("voltro_parity_a", "a4"), t(), T(e).toEqual([
|
|
466
466
|
"insert",
|
|
467
467
|
"update",
|
|
468
468
|
"delete"
|
|
469
469
|
]);
|
|
470
|
-
}),
|
|
470
|
+
}), E("binding more than 10 change listeners emits no MaxListeners warning", async () => {
|
|
471
471
|
let e = [], t = (t) => {
|
|
472
472
|
e.push(t.name);
|
|
473
473
|
};
|
|
474
474
|
process.on("warning", t);
|
|
475
|
-
let
|
|
475
|
+
let n = Array.from({ length: 32 }, () => r.store.onChange(() => {}));
|
|
476
476
|
await new Promise((e) => setImmediate(e));
|
|
477
|
-
for (let e of
|
|
478
|
-
process.off("warning", t),
|
|
479
|
-
}),
|
|
480
|
-
let e = [], t =
|
|
481
|
-
await
|
|
477
|
+
for (let e of n) e();
|
|
478
|
+
process.off("warning", t), T(e.filter((e) => e === "MaxListenersExceededWarning"), "the change bus still warns at 11 listeners — `raiseChangeListenerCeiling` is not wired into this dialect’s store").toEqual([]);
|
|
479
|
+
}), E("transactional rolls back on throw + drops queued events", async () => {
|
|
480
|
+
let e = [], t = r.store.onChange((t) => e.push(t.op));
|
|
481
|
+
await T(r.store.transactional(async (e) => {
|
|
482
482
|
throw await e.insert("voltro_parity_a", {
|
|
483
483
|
id: "rollback",
|
|
484
484
|
title: "r",
|
|
485
485
|
count: 0
|
|
486
486
|
}), Error("intentional rollback");
|
|
487
|
-
})).rejects.toThrow("intentional rollback"), t(),
|
|
488
|
-
let
|
|
489
|
-
|
|
490
|
-
}),
|
|
491
|
-
let e = [], t =
|
|
492
|
-
await
|
|
487
|
+
})).rejects.toThrow("intentional rollback"), t(), T(e).toEqual([]);
|
|
488
|
+
let n = await r.store.query(M("voltro_parity_a"));
|
|
489
|
+
T(n.length).toBe(0);
|
|
490
|
+
}), E("transactional commit drains queued events in order", async () => {
|
|
491
|
+
let e = [], t = r.store.onChange((t) => e.push(t.op));
|
|
492
|
+
await r.store.transactional(async (e) => {
|
|
493
493
|
await e.insert("voltro_parity_a", {
|
|
494
494
|
id: "commit1",
|
|
495
495
|
title: "a",
|
|
@@ -499,13 +499,13 @@ var L = u("voltro_parity_composite", {
|
|
|
499
499
|
title: "b",
|
|
500
500
|
count: 2
|
|
501
501
|
});
|
|
502
|
-
}), t(),
|
|
503
|
-
}),
|
|
504
|
-
await
|
|
502
|
+
}), t(), T(e).toEqual(["insert", "insert"]);
|
|
503
|
+
}), E("upsert inserts a new row, then updates on conflict — no duplicate", async () => {
|
|
504
|
+
await r.store.upsert("voltro_parity_a", {
|
|
505
505
|
id: "u1",
|
|
506
506
|
title: "first",
|
|
507
507
|
count: 1
|
|
508
|
-
}, { conflictColumns: ["id"] }), await
|
|
508
|
+
}, { conflictColumns: ["id"] }), await r.store.upsert("voltro_parity_a", {
|
|
509
509
|
id: "u1",
|
|
510
510
|
title: "second",
|
|
511
511
|
count: 2
|
|
@@ -513,14 +513,291 @@ var L = u("voltro_parity_composite", {
|
|
|
513
513
|
conflictColumns: ["id"],
|
|
514
514
|
update: ["title", "count"]
|
|
515
515
|
});
|
|
516
|
-
let e = await
|
|
517
|
-
|
|
516
|
+
let e = await r.store.query(M("voltro_parity_a"));
|
|
517
|
+
T(e.length).toBe(1), T(e[0]).toMatchObject({
|
|
518
518
|
id: "u1",
|
|
519
519
|
title: "second",
|
|
520
520
|
count: 2
|
|
521
521
|
});
|
|
522
|
-
}),
|
|
523
|
-
|
|
522
|
+
}), E.each([
|
|
523
|
+
"done",
|
|
524
|
+
"",
|
|
525
|
+
"null",
|
|
526
|
+
"true",
|
|
527
|
+
"42",
|
|
528
|
+
"[1,2]",
|
|
529
|
+
"{\"a\":1}",
|
|
530
|
+
"\"quoted\""
|
|
531
|
+
])("JSON scalar string %j survives insert and update", async (e) => {
|
|
532
|
+
let t = R.tableName, n = await r.store.insert(t, {
|
|
533
|
+
id: "json-string",
|
|
534
|
+
title: "before",
|
|
535
|
+
metadata: e,
|
|
536
|
+
labels: []
|
|
537
|
+
});
|
|
538
|
+
T(n.metadata).toBe(e), T((await r.store.query(M(t)))[0]?.metadata).toBe(e), await r.store.transactional(async (n) => {
|
|
539
|
+
let r = await n.update(t, "json-string", { metadata: `${e}\nupdated` });
|
|
540
|
+
T(r?.metadata).toBe(`${e}\nupdated`);
|
|
541
|
+
}), T((await r.store.query(M(t)))[0]?.metadata).toBe(`${e}\nupdated`);
|
|
542
|
+
}), E("JSON validity checks are not introspected as enum membership", async () => {
|
|
543
|
+
let e = (await r.run(g.gen(function* () {
|
|
544
|
+
let e = yield* D.SqlClient;
|
|
545
|
+
return yield* A(e);
|
|
546
|
+
}))).tables.find((e) => e.name === R.tableName)?.columns.find((e) => e.name === "metadata");
|
|
547
|
+
T(e).toBeDefined(), T(e?.oneOf).toBeUndefined(), n.dialect.id === "mssql" && (T(e?.jsonValidation).toBe("value"), T(e?.jsonChecks).toEqual([{
|
|
548
|
+
name: T.any(String),
|
|
549
|
+
validation: "value",
|
|
550
|
+
enabled: !0,
|
|
551
|
+
trusted: !0
|
|
552
|
+
}]));
|
|
553
|
+
}), n.dialect.id === "mssql" && E("JSON check catalog includes table-level names and enforcement state", async () => {
|
|
554
|
+
let e = (e) => r.run(g.gen(function* () {
|
|
555
|
+
yield* (yield* D.SqlClient).unsafe(e);
|
|
556
|
+
})), t = () => r.run(g.gen(function* () {
|
|
557
|
+
let e = yield* D.SqlClient;
|
|
558
|
+
return (yield* A(e)).tables.find((e) => e.name === "voltro_parity_json_checks")?.columns.find((e) => e.name === "metadata")?.jsonChecks;
|
|
559
|
+
}));
|
|
560
|
+
await e("CREATE TABLE voltro_parity_json_checks (id INT PRIMARY KEY, metadata NVARCHAR(MAX), CONSTRAINT json_container_probe CHECK (metadata IS NULL OR ISJSON(metadata) = 1), CONSTRAINT custom_json_probe CHECK (metadata IS NULL OR LEN(metadata) > 2))");
|
|
561
|
+
try {
|
|
562
|
+
T(await t()).toEqual([{
|
|
563
|
+
name: "json_container_probe",
|
|
564
|
+
validation: "container",
|
|
565
|
+
enabled: !0,
|
|
566
|
+
trusted: !0
|
|
567
|
+
}]), await e("ALTER TABLE voltro_parity_json_checks NOCHECK CONSTRAINT json_container_probe"), T(await t()).toEqual([{
|
|
568
|
+
name: "json_container_probe",
|
|
569
|
+
validation: "container",
|
|
570
|
+
enabled: !1,
|
|
571
|
+
trusted: !1
|
|
572
|
+
}]), await e("ALTER TABLE voltro_parity_json_checks CHECK CONSTRAINT json_container_probe"), T(await t()).toEqual([{
|
|
573
|
+
name: "json_container_probe",
|
|
574
|
+
validation: "container",
|
|
575
|
+
enabled: !0,
|
|
576
|
+
trusted: !1
|
|
577
|
+
}]);
|
|
578
|
+
} finally {
|
|
579
|
+
await e("DROP TABLE voltro_parity_json_checks");
|
|
580
|
+
}
|
|
581
|
+
}), n.dialect.id === "mssql" && E.each([
|
|
582
|
+
{
|
|
583
|
+
invalid: !1,
|
|
584
|
+
rename: !1
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
invalid: !0,
|
|
588
|
+
rename: !1
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
invalid: !1,
|
|
592
|
+
rename: !0
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
invalid: !0,
|
|
596
|
+
rename: !0
|
|
597
|
+
}
|
|
598
|
+
])("JSON check migration preserves data and is atomic (invalid=$invalid, rename=$rename)", async ({ invalid: t, rename: n }) => {
|
|
599
|
+
let i = "voltro_parity_json_upgrade", a = n ? "renamed_metadata" : "metadata", s = [f(i, {
|
|
600
|
+
id: o(),
|
|
601
|
+
[a]: n ? c().nullable().renamedFrom("metadata") : c().nullable()
|
|
602
|
+
})], l = (e) => r.run(g.gen(function* () {
|
|
603
|
+
return yield* (yield* D.SqlClient).unsafe(e);
|
|
604
|
+
})), u = (e) => A(e).pipe(g.map((e) => j({
|
|
605
|
+
declared: s,
|
|
606
|
+
dialect: "mssql",
|
|
607
|
+
live: { tables: e.tables.filter((e) => e.name === i) }
|
|
608
|
+
}))), d = () => r.run(D.SqlClient.pipe(g.flatMap(u)));
|
|
609
|
+
await r.migrate([e]), await l(`CREATE TABLE ${i} (id VARCHAR(64) PRIMARY KEY, metadata NVARCHAR(MAX) NULL, CONSTRAINT upgrade_json_old CHECK (metadata IS NULL OR ISJSON(metadata)=1), CONSTRAINT upgrade_json_custom CHECK NOT FOR REPLICATION (metadata IS NULL OR (LEN(metadata)<100 AND metadata<>N'"[metadata]"')))`);
|
|
610
|
+
try {
|
|
611
|
+
let e = () => l(`SELECT is_disabled AS disabled, is_not_trusted AS untrusted, is_not_for_replication AS notForReplication FROM sys.check_constraints WHERE parent_object_id=OBJECT_ID('${i}') AND name='upgrade_json_custom'`), o = await e();
|
|
612
|
+
T(o).toHaveLength(1), T(o[0]).toMatchObject({
|
|
613
|
+
disabled: !1,
|
|
614
|
+
notForReplication: !0
|
|
615
|
+
}), t && await l(`ALTER TABLE ${i} NOCHECK CONSTRAINT upgrade_json_old`), await l(`INSERT INTO ${i} (id,metadata) VALUES ('kept','${t ? "invalid" : "{\"kept\":true}"}')`);
|
|
616
|
+
let c = await d();
|
|
617
|
+
T(c.operations.map((e) => e.op)).toEqual([...n ? [{
|
|
618
|
+
kind: "rename-column",
|
|
619
|
+
table: i,
|
|
620
|
+
from: "metadata",
|
|
621
|
+
to: a
|
|
622
|
+
}] : [], {
|
|
623
|
+
kind: "reconcile-json-check",
|
|
624
|
+
table: i,
|
|
625
|
+
column: a,
|
|
626
|
+
validation: "value",
|
|
627
|
+
previousValidation: t ? "unverified" : "container",
|
|
628
|
+
dropConstraints: ["upgrade_json_old"]
|
|
629
|
+
}]);
|
|
630
|
+
let f = r.run(g.gen(function* () {
|
|
631
|
+
let e = yield* D.SqlClient;
|
|
632
|
+
return yield* O(e, c, {
|
|
633
|
+
declared: s,
|
|
634
|
+
appliedBy: "json-parity",
|
|
635
|
+
environment: "dev",
|
|
636
|
+
source: "auto-diff",
|
|
637
|
+
replan: u
|
|
638
|
+
});
|
|
639
|
+
}));
|
|
640
|
+
t ? (await T(f).rejects.toThrow("op=reconcile-json-check"), T((await d()).operations.map((e) => e.op)).toEqual(c.operations.map((e) => e.op))) : (await f, T((await d()).operations).toEqual([]), await l(`INSERT INTO ${i} (id,${a}) VALUES ('scalar','"done"')`), await T(l(`INSERT INTO ${i} (id,${a}) VALUES ('too-long','"${"x".repeat(110)}"')`)).rejects.toThrow(), await T(l(`INSERT INTO ${i} (id,${a}) VALUES ('literal','"[metadata]"')`)).rejects.toThrow()), T(await l(`SELECT ${t ? "metadata" : a} AS metadata FROM ${i} WHERE id='kept'`)).toEqual([{ metadata: t ? "invalid" : "{\"kept\":true}" }]), T(await l(`SELECT name FROM sys.check_constraints WHERE parent_object_id=OBJECT_ID('${i}') AND name='upgrade_json_custom'`)).toEqual([{ name: "upgrade_json_custom" }]), T(await e()).toEqual(o);
|
|
641
|
+
} finally {
|
|
642
|
+
await l(`DROP TABLE ${i}`);
|
|
643
|
+
}
|
|
644
|
+
}), n.dialect.id === "mssql" && E.each([
|
|
645
|
+
"trusted",
|
|
646
|
+
"untrusted",
|
|
647
|
+
"disabled"
|
|
648
|
+
])("column rename preserves %s application CHECK state", async (t) => {
|
|
649
|
+
let n = "voltro_parity_rename_checks", i = [f(n, {
|
|
650
|
+
id: o(),
|
|
651
|
+
renamed: p().nullable().renamedFrom("original")
|
|
652
|
+
})], a = (e) => r.run(g.gen(function* () {
|
|
653
|
+
return yield* (yield* D.SqlClient).unsafe(e);
|
|
654
|
+
})), s = (e) => A(e).pipe(g.map((e) => j({
|
|
655
|
+
declared: i,
|
|
656
|
+
dialect: "mssql",
|
|
657
|
+
live: { tables: e.tables.filter((e) => e.name === n) }
|
|
658
|
+
})));
|
|
659
|
+
await r.migrate([e]), await a(`CREATE TABLE ${n} (id VARCHAR(64) PRIMARY KEY, original NVARCHAR(MAX) NULL, CONSTRAINT rename_state_check CHECK (LEN(original)<10))`);
|
|
660
|
+
try {
|
|
661
|
+
t !== "trusted" && await a(`ALTER TABLE ${n} NOCHECK CONSTRAINT rename_state_check`), t === "untrusted" && await a(`ALTER TABLE ${n} CHECK CONSTRAINT rename_state_check`);
|
|
662
|
+
let e = () => a(`SELECT name,is_disabled AS disabled,is_not_trusted AS untrusted FROM sys.check_constraints WHERE parent_object_id=OBJECT_ID('${n}')`), o = await e();
|
|
663
|
+
T(o).toEqual([{
|
|
664
|
+
name: "rename_state_check",
|
|
665
|
+
disabled: t === "disabled",
|
|
666
|
+
untrusted: t !== "trusted"
|
|
667
|
+
}]), await a(`INSERT INTO ${n} (id,original) VALUES ('kept','${t === "disabled" ? "long invalid existing value" : "valid"}')`), await r.run(g.gen(function* () {
|
|
668
|
+
let e = yield* D.SqlClient, t = yield* s(e);
|
|
669
|
+
T(t.operations.map((e) => e.op.kind)).toEqual(["rename-column"]), yield* O(e, t, {
|
|
670
|
+
declared: i,
|
|
671
|
+
appliedBy: "rename-parity",
|
|
672
|
+
environment: "dev",
|
|
673
|
+
source: "auto-diff",
|
|
674
|
+
replan: s
|
|
675
|
+
}), T((yield* s(e)).operations).toEqual([]);
|
|
676
|
+
})), T(await e()).toEqual(o), T(await a(`SELECT renamed FROM ${n} WHERE id='kept'`)).toEqual([{ renamed: t === "disabled" ? "long invalid existing value" : "valid" }]);
|
|
677
|
+
let c = a(`INSERT INTO ${n} (id,renamed) VALUES ('probe','another invalid value')`);
|
|
678
|
+
t === "disabled" ? await c : await T(c).rejects.toThrow();
|
|
679
|
+
} finally {
|
|
680
|
+
await a(`DROP TABLE ${n}`);
|
|
681
|
+
}
|
|
682
|
+
}), n.dialect.id === "mssql" && E.each(["create", "add"])("JSON checks are complete after %s-column DDL", async (n) => {
|
|
683
|
+
let i = "voltro_parity_json_ddl", a = [f(i, {
|
|
684
|
+
id: o(),
|
|
685
|
+
metadata: c().nullable(),
|
|
686
|
+
labels: t(p()).nullable()
|
|
687
|
+
})], s = (e) => r.run(g.gen(function* () {
|
|
688
|
+
return yield* (yield* D.SqlClient).unsafe(e);
|
|
689
|
+
})), l = (e) => A(e).pipe(g.map((e) => j({
|
|
690
|
+
declared: a,
|
|
691
|
+
dialect: "mssql",
|
|
692
|
+
live: { tables: e.tables.filter((e) => e.name === i) }
|
|
693
|
+
})));
|
|
694
|
+
await r.migrate([e]), n === "add" && (await s(`CREATE TABLE ${i} (id VARCHAR(64) PRIMARY KEY)`), await s(`INSERT INTO ${i} (id) VALUES ('existing')`));
|
|
695
|
+
try {
|
|
696
|
+
await r.run(g.gen(function* () {
|
|
697
|
+
let e = yield* D.SqlClient, t = yield* l(e);
|
|
698
|
+
T(t.operations.map((e) => e.op.kind)).toEqual(n === "create" ? ["create-table"] : ["add-column", "add-column"]), yield* O(e, t, {
|
|
699
|
+
declared: a,
|
|
700
|
+
appliedBy: "json-ddl",
|
|
701
|
+
environment: "dev",
|
|
702
|
+
source: "auto-diff",
|
|
703
|
+
replan: l
|
|
704
|
+
}), T((yield* l(e)).operations).toEqual([]);
|
|
705
|
+
})), await s(`INSERT INTO ${i} (id,metadata,labels) VALUES ('valid','"done"','["x"]')`), await T(s(`INSERT INTO ${i} (id,metadata) VALUES ('invalid','1,2')`)).rejects.toThrow(), await T(s(`INSERT INTO ${i} (id,labels) VALUES ('invalid-array','"x"')`)).rejects.toThrow(), n === "add" && T(await s(`SELECT metadata,labels FROM ${i} WHERE id='existing'`)).toEqual([{
|
|
706
|
+
metadata: null,
|
|
707
|
+
labels: null
|
|
708
|
+
}]);
|
|
709
|
+
} finally {
|
|
710
|
+
await s(`DROP TABLE IF EXISTS ${i}`);
|
|
711
|
+
}
|
|
712
|
+
}), E.each([
|
|
713
|
+
"",
|
|
714
|
+
"1,2",
|
|
715
|
+
"1,\"other\":2",
|
|
716
|
+
"1,\"v\":2",
|
|
717
|
+
"{bad}",
|
|
718
|
+
"undefined"
|
|
719
|
+
])("JSON check rejects raw invalid value %j", async (e) => {
|
|
720
|
+
await T(r.run(g.gen(function* () {
|
|
721
|
+
let t = yield* D.SqlClient;
|
|
722
|
+
yield* t`INSERT INTO ${t(R.tableName)} (${t("id")}, ${t("title")}, ${t("metadata")}) VALUES (${"invalid-json"}, ${"probe"}, ${e})`;
|
|
723
|
+
}))).rejects.toThrow();
|
|
724
|
+
}), w.each(["direct", "transactional"])("upsert recorder atomicity (%s)", (e) => {
|
|
725
|
+
E.each([
|
|
726
|
+
{
|
|
727
|
+
kind: "native",
|
|
728
|
+
existing: !1
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
kind: "native",
|
|
732
|
+
existing: !0
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
kind: "functional",
|
|
736
|
+
existing: !1
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
kind: "functional",
|
|
740
|
+
existing: !0
|
|
741
|
+
}
|
|
742
|
+
])("$kind upsert (existing=$existing) rolls back its write and recorder append", async ({ kind: t, existing: i }) => {
|
|
743
|
+
let a = r.store, o = "voltro_parity_a", s = {
|
|
744
|
+
id: "recorded",
|
|
745
|
+
title: "before",
|
|
746
|
+
count: 0
|
|
747
|
+
};
|
|
748
|
+
i && await a.insert(o, s);
|
|
749
|
+
let c = `parity-upsert-${n.dialect.id}`, l = [], f = a.onChange((e) => {
|
|
750
|
+
e.table === o && l.push(e.op);
|
|
751
|
+
});
|
|
752
|
+
u(c, {
|
|
753
|
+
tables: [o],
|
|
754
|
+
ownTables: ["voltro_parity_b"],
|
|
755
|
+
recorder: async (e, t) => {
|
|
756
|
+
throw T(t.traceId).toBe("recorder-trace"), T(t.subjectId).toBe("recorder-subject"), await e.append("voltro_parity_b", {
|
|
757
|
+
id: "recorder-append",
|
|
758
|
+
title: "trail"
|
|
759
|
+
}), Error("recorder refused");
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
let p = (e) => e.upsert(o, {
|
|
763
|
+
id: "recorded",
|
|
764
|
+
title: "new",
|
|
765
|
+
count: 1
|
|
766
|
+
}, {
|
|
767
|
+
conflictColumns: ["id"],
|
|
768
|
+
update: t === "native" ? ["title"] : () => ({ title: "computed" })
|
|
769
|
+
});
|
|
770
|
+
try {
|
|
771
|
+
await T(d({
|
|
772
|
+
traceId: "recorder-trace",
|
|
773
|
+
subjectId: "recorder-subject"
|
|
774
|
+
}, () => e === "direct" ? p(a) : a.transactional(p))).rejects.toThrow("recorder refused"), T(await a.query(M(o))).toEqual(i ? [s] : []), T(await a.query(M("voltro_parity_b"))).toEqual([]), T(l).toEqual([]);
|
|
775
|
+
} finally {
|
|
776
|
+
h(c), f();
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
}), E("structured write returns and change images decode exactly once", async () => {
|
|
780
|
+
let e = r.store, t = R.tableName, n = [], i = e.onChange((e) => {
|
|
781
|
+
e.table === t && n.push((e.new ?? e.old)?.metadata);
|
|
782
|
+
}), o = (e) => ({
|
|
783
|
+
id: e,
|
|
784
|
+
title: "before",
|
|
785
|
+
metadata: "null",
|
|
786
|
+
labels: ["x"]
|
|
787
|
+
});
|
|
788
|
+
try {
|
|
789
|
+
T((await e.insertMany(t, [o("one"), o("two")])).map((e) => e.metadata)).toEqual(["null", "null"]), T((await e.upsert(t, o("one"), {
|
|
790
|
+
conflictColumns: ["id"],
|
|
791
|
+
update: ["metadata"]
|
|
792
|
+
})).metadata).toBe("null"), T((await e.insertIgnore(t, o("three"), { conflictColumns: ["id"] })).metadata).toBe("null"), T((await e.insertIgnore(t, o("three"), { conflictColumns: ["id"] })).metadata).toBe("null"), await e.upsert(t, o("one"), {
|
|
793
|
+
conflictColumns: ["id"],
|
|
794
|
+
update: (e) => (T(e.metadata).toBe("null"), { metadata: "null" })
|
|
795
|
+
}), await e.updateMany(t, { metadata: "null" }, { where: a("id", "one") }), await e.delete(t, "two"), await e.deleteMany(t, { where: a("id", "three") }), T(n.length).toBeGreaterThanOrEqual(8), T(n.every((e) => e === "null")).toBe(!0);
|
|
796
|
+
} finally {
|
|
797
|
+
i();
|
|
798
|
+
}
|
|
799
|
+
}), w.each(["direct", "transactional"])("JSON patch and column onUpdate (%s)", (e) => {
|
|
800
|
+
let t = (t) => e === "transactional" ? r.store.transactional(t) : t(r.store), n = [
|
|
524
801
|
{
|
|
525
802
|
name: "shallow object merge retains unrelated keys and nulls",
|
|
526
803
|
before: {
|
|
@@ -738,7 +1015,7 @@ var L = u("voltro_parity_composite", {
|
|
|
738
1015
|
}
|
|
739
1016
|
}))
|
|
740
1017
|
];
|
|
741
|
-
|
|
1018
|
+
E.each(n)("$name with companion fields in the same write", async ({ before: e, path: n, value: r, after: i }) => {
|
|
742
1019
|
await t(async (t) => {
|
|
743
1020
|
await t.insert("voltro_parity_values", {
|
|
744
1021
|
id: "patch",
|
|
@@ -750,17 +1027,19 @@ var L = u("voltro_parity_composite", {
|
|
|
750
1027
|
title: "after",
|
|
751
1028
|
labels: []
|
|
752
1029
|
});
|
|
753
|
-
|
|
1030
|
+
T(a).toMatchObject({
|
|
754
1031
|
id: "patch",
|
|
755
|
-
title: "after"
|
|
756
|
-
|
|
1032
|
+
title: "after",
|
|
1033
|
+
metadata: i,
|
|
1034
|
+
labels: []
|
|
1035
|
+
}), T(await t.query(M("voltro_parity_values"))).toEqual([{
|
|
757
1036
|
id: "patch",
|
|
758
1037
|
title: "after",
|
|
759
1038
|
metadata: i,
|
|
760
1039
|
labels: []
|
|
761
1040
|
}]);
|
|
762
1041
|
});
|
|
763
|
-
}),
|
|
1042
|
+
}), E("refuses companion primary-key or JSON-column replacement before writing", async () => {
|
|
764
1043
|
await t(async (e) => {
|
|
765
1044
|
let t = {
|
|
766
1045
|
id: "refuse",
|
|
@@ -769,16 +1048,16 @@ var L = u("voltro_parity_composite", {
|
|
|
769
1048
|
labels: []
|
|
770
1049
|
};
|
|
771
1050
|
await e.insert("voltro_parity_values", t);
|
|
772
|
-
for (let t of [{ id: "renamed" }, { metadata: {} }]) await
|
|
773
|
-
|
|
1051
|
+
for (let t of [{ id: "renamed" }, { metadata: {} }]) await T(e.patchJson("voltro_parity_values", "refuse", "metadata.value", 1, t)).rejects.toThrow("cannot rewrite");
|
|
1052
|
+
T(await e.query(M("voltro_parity_values"))).toEqual([t]), T(await e.patchJson("voltro_parity_values", "missing", "metadata.value", 1, { title: "absent" })).toBeNull();
|
|
774
1053
|
});
|
|
775
|
-
}),
|
|
1054
|
+
}), E("does not confuse JSON columns named key/value with iterator columns", async () => {
|
|
776
1055
|
await t(async (e) => {
|
|
777
|
-
await e.insert(
|
|
1056
|
+
await e.insert(z.tableName, {
|
|
778
1057
|
id: "names",
|
|
779
1058
|
key: { "a\"b": { keep: !0 } },
|
|
780
1059
|
value: { "c\\d": { keep: !1 } }
|
|
781
|
-
}), await e.patchJson(
|
|
1060
|
+
}), await e.patchJson(z.tableName, "names", "key.a\"b.changed", !1), await e.patchJson(z.tableName, "names", "value.c\\d.changed", !0), T(await e.query(M(z.tableName))).toEqual([{
|
|
782
1061
|
id: "names",
|
|
783
1062
|
key: { "a\"b": {
|
|
784
1063
|
keep: !0,
|
|
@@ -790,14 +1069,14 @@ var L = u("voltro_parity_composite", {
|
|
|
790
1069
|
} }
|
|
791
1070
|
}]);
|
|
792
1071
|
});
|
|
793
|
-
}),
|
|
794
|
-
let t =
|
|
795
|
-
tableName:
|
|
796
|
-
fields:
|
|
1072
|
+
}), E("executes application onUpdate across keyed, bulk, upsert and JSON writes", async () => {
|
|
1073
|
+
let t = y([{
|
|
1074
|
+
tableName: I.tableName,
|
|
1075
|
+
fields: I.fields
|
|
797
1076
|
}, {
|
|
798
|
-
tableName:
|
|
799
|
-
fields:
|
|
800
|
-
}]),
|
|
1077
|
+
tableName: R.tableName,
|
|
1078
|
+
fields: R.fields
|
|
1079
|
+
}]), n = x(r.store, {
|
|
801
1080
|
subject: {
|
|
802
1081
|
type: "anonymous",
|
|
803
1082
|
id: null,
|
|
@@ -823,22 +1102,22 @@ var L = u("voltro_parity_composite", {
|
|
|
823
1102
|
conflictColumns: ["id"],
|
|
824
1103
|
update: n === "callback" ? () => ({ title: "edited" }) : ["title"]
|
|
825
1104
|
});
|
|
826
|
-
let r = await e.query(
|
|
827
|
-
|
|
1105
|
+
let r = await e.query(M("voltro_parity_d"));
|
|
1106
|
+
T(r).toHaveLength(4);
|
|
828
1107
|
for (let e of r) {
|
|
829
|
-
|
|
1108
|
+
T(e).toMatchObject({
|
|
830
1109
|
title: "edited",
|
|
831
1110
|
n: 9
|
|
832
1111
|
});
|
|
833
1112
|
let t = e.at instanceof Date ? e.at.getTime() : new Date(String(e.at)).getTime();
|
|
834
|
-
|
|
1113
|
+
T(t).toBeGreaterThanOrEqual(n), T(t).toBeLessThanOrEqual(Date.now());
|
|
835
1114
|
}
|
|
836
1115
|
await e.insert("voltro_parity_values", {
|
|
837
1116
|
id: "automatic",
|
|
838
1117
|
title: "original",
|
|
839
1118
|
metadata: { keep: !0 },
|
|
840
1119
|
labels: []
|
|
841
|
-
}), await e.patchJson("voltro_parity_values", "automatic", "metadata.child.value", !1),
|
|
1120
|
+
}), await e.patchJson("voltro_parity_values", "automatic", "metadata.child.value", !1), T(await e.query(M("voltro_parity_values"))).toEqual([{
|
|
842
1121
|
id: "automatic",
|
|
843
1122
|
title: "automatic",
|
|
844
1123
|
metadata: {
|
|
@@ -846,7 +1125,7 @@ var L = u("voltro_parity_composite", {
|
|
|
846
1125
|
child: { value: !1 }
|
|
847
1126
|
},
|
|
848
1127
|
labels: []
|
|
849
|
-
}]),
|
|
1128
|
+
}]), T(await e.upsert("voltro_parity_d", {
|
|
850
1129
|
id: "new",
|
|
851
1130
|
title: "insert"
|
|
852
1131
|
}, {
|
|
@@ -854,35 +1133,35 @@ var L = u("voltro_parity_composite", {
|
|
|
854
1133
|
update: []
|
|
855
1134
|
})).toMatchObject({ at: null });
|
|
856
1135
|
};
|
|
857
|
-
e === "transactional" ? await
|
|
1136
|
+
e === "transactional" ? await n.transactional(i) : await i(n);
|
|
858
1137
|
});
|
|
859
|
-
}),
|
|
1138
|
+
}), E("JSON patch companion failure rolls back the JSON write too", async () => {
|
|
860
1139
|
let e = {
|
|
861
1140
|
id: "atomic-json",
|
|
862
1141
|
title: "keep",
|
|
863
1142
|
metadata: { keep: !0 },
|
|
864
1143
|
labels: []
|
|
865
1144
|
};
|
|
866
|
-
await
|
|
867
|
-
}),
|
|
868
|
-
await
|
|
1145
|
+
await r.store.insert("voltro_parity_values", e), await T(r.store.patchJson("voltro_parity_values", e.id, "metadata.changed", !0, { title: null })).rejects.toThrow(), T(await r.store.query(M("voltro_parity_values"))).toEqual([e]);
|
|
1146
|
+
}), E("concurrent JSON patches preserve every sibling and the companion write", async () => {
|
|
1147
|
+
await r.store.insert("voltro_parity_values", {
|
|
869
1148
|
id: "concurrent-json",
|
|
870
1149
|
title: "before",
|
|
871
1150
|
metadata: {},
|
|
872
1151
|
labels: []
|
|
873
|
-
}), await Promise.all(Array.from({ length: 8 }, (e, t) =>
|
|
1152
|
+
}), await Promise.all(Array.from({ length: 8 }, (e, t) => r.store.transactional((e) => e.patchJson("voltro_parity_values", "concurrent-json", `metadata.key${t}`, t, { title: "after" })))), T(await r.store.query(M("voltro_parity_values"))).toEqual([{
|
|
874
1153
|
id: "concurrent-json",
|
|
875
1154
|
title: "after",
|
|
876
1155
|
metadata: Object.fromEntries(Array.from({ length: 8 }, (e, t) => [`key${t}`, t])),
|
|
877
1156
|
labels: []
|
|
878
1157
|
}]);
|
|
879
|
-
}),
|
|
880
|
-
await
|
|
1158
|
+
}), E("upsert on a NON-primary-key unique constraint updates that row", async () => {
|
|
1159
|
+
await r.store.upsert("voltro_parity_upsert", {
|
|
881
1160
|
id: "up_first",
|
|
882
1161
|
teamId: "t-1",
|
|
883
1162
|
year: 2026,
|
|
884
1163
|
hours: 10
|
|
885
|
-
}, { conflictColumns: ["teamId", "year"] }), await
|
|
1164
|
+
}, { conflictColumns: ["teamId", "year"] }), await r.store.upsert("voltro_parity_upsert", {
|
|
886
1165
|
id: "up_second",
|
|
887
1166
|
teamId: "t-1",
|
|
888
1167
|
year: 2026,
|
|
@@ -891,17 +1170,17 @@ var L = u("voltro_parity_composite", {
|
|
|
891
1170
|
conflictColumns: ["teamId", "year"],
|
|
892
1171
|
update: ["hours"]
|
|
893
1172
|
});
|
|
894
|
-
let e = await
|
|
895
|
-
|
|
1173
|
+
let e = await r.store.query(M("voltro_parity_upsert"));
|
|
1174
|
+
T(e.length, "the second upsert inserted a duplicate instead of updating").toBe(1), T(e[0]).toMatchObject({
|
|
896
1175
|
id: "up_first",
|
|
897
1176
|
teamId: "t-1",
|
|
898
1177
|
year: 2026,
|
|
899
1178
|
hours: 111
|
|
900
1179
|
});
|
|
901
|
-
}),
|
|
902
|
-
let t = (t) => e === "transactional" ?
|
|
903
|
-
|
|
904
|
-
let e = /* @__PURE__ */ new Date("2026-09-05T10:20:30.000Z"),
|
|
1180
|
+
}), w.each(["direct", "transactional"])("upsert updateValues (%s)", (e) => {
|
|
1181
|
+
let t = (t) => e === "transactional" ? r.store.transactional(t) : t(r.store);
|
|
1182
|
+
E("keeps INSERT defaults and writes omitted columns only on conflict", async () => {
|
|
1183
|
+
let e = /* @__PURE__ */ new Date("2026-09-05T10:20:30.000Z"), n = {
|
|
905
1184
|
conflictColumns: ["id"],
|
|
906
1185
|
update: [],
|
|
907
1186
|
updateValues: {
|
|
@@ -910,11 +1189,11 @@ var L = u("voltro_parity_composite", {
|
|
|
910
1189
|
}
|
|
911
1190
|
};
|
|
912
1191
|
await t(async (t) => {
|
|
913
|
-
let
|
|
1192
|
+
let r = await t.upsert("voltro_parity_d", {
|
|
914
1193
|
id: "constant",
|
|
915
1194
|
title: "Lobby"
|
|
916
|
-
},
|
|
917
|
-
|
|
1195
|
+
}, n);
|
|
1196
|
+
T(r).toMatchObject({
|
|
918
1197
|
id: "constant",
|
|
919
1198
|
title: "Lobby",
|
|
920
1199
|
at: null,
|
|
@@ -923,20 +1202,20 @@ var L = u("voltro_parity_composite", {
|
|
|
923
1202
|
let i = await t.upsert("voltro_parity_d", {
|
|
924
1203
|
id: "constant",
|
|
925
1204
|
title: "Do not overwrite"
|
|
926
|
-
},
|
|
927
|
-
|
|
1205
|
+
}, n);
|
|
1206
|
+
T(i).toMatchObject({
|
|
928
1207
|
id: "constant",
|
|
929
1208
|
title: "Lobby",
|
|
930
1209
|
n: 42
|
|
931
|
-
}),
|
|
1210
|
+
}), T(new Date(String(i.at)).toISOString()).toBe(e.toISOString());
|
|
932
1211
|
});
|
|
933
|
-
let [i] = await
|
|
934
|
-
|
|
1212
|
+
let [i] = await r.store.query(M("voltro_parity_d"));
|
|
1213
|
+
T(i).toMatchObject({
|
|
935
1214
|
id: "constant",
|
|
936
1215
|
title: "Lobby",
|
|
937
1216
|
n: 42
|
|
938
|
-
}),
|
|
939
|
-
}),
|
|
1217
|
+
}), T(new Date(String(i.at)).toISOString()).toBe(e.toISOString());
|
|
1218
|
+
}), E.each([
|
|
940
1219
|
"omitted",
|
|
941
1220
|
"empty",
|
|
942
1221
|
"columns",
|
|
@@ -959,19 +1238,19 @@ var L = u("voltro_parity_composite", {
|
|
|
959
1238
|
...n === void 0 ? {} : { update: n },
|
|
960
1239
|
updateValues: { hours: 42 }
|
|
961
1240
|
});
|
|
962
|
-
|
|
1241
|
+
T(r).toMatchObject({
|
|
963
1242
|
id: "original",
|
|
964
1243
|
teamId: "t1",
|
|
965
1244
|
year: 2026,
|
|
966
1245
|
hours: 42
|
|
967
1246
|
});
|
|
968
|
-
}),
|
|
1247
|
+
}), T(await r.store.query(M("voltro_parity_upsert"))).toEqual([T.objectContaining({
|
|
969
1248
|
id: "original",
|
|
970
1249
|
teamId: "t1",
|
|
971
1250
|
year: 2026,
|
|
972
1251
|
hours: 42
|
|
973
1252
|
})]);
|
|
974
|
-
}),
|
|
1253
|
+
}), E("binds explicit NULL to timestamp/int columns and omits undefined assignments", async () => {
|
|
975
1254
|
await t(async (e) => {
|
|
976
1255
|
await e.insert("voltro_parity_d", {
|
|
977
1256
|
id: "nulls",
|
|
@@ -993,12 +1272,12 @@ var L = u("voltro_parity_composite", {
|
|
|
993
1272
|
n: void 0
|
|
994
1273
|
}
|
|
995
1274
|
});
|
|
996
|
-
|
|
1275
|
+
T(n).toMatchObject({
|
|
997
1276
|
id: "nulls",
|
|
998
1277
|
title: "",
|
|
999
1278
|
at: null,
|
|
1000
1279
|
n: 9
|
|
1001
|
-
}),
|
|
1280
|
+
}), T(await e.upsert("voltro_parity_d", t, {
|
|
1002
1281
|
conflictColumns: ["id"],
|
|
1003
1282
|
update: [],
|
|
1004
1283
|
updateValues: { n: null }
|
|
@@ -1009,7 +1288,7 @@ var L = u("voltro_parity_composite", {
|
|
|
1009
1288
|
n: null
|
|
1010
1289
|
});
|
|
1011
1290
|
});
|
|
1012
|
-
}),
|
|
1291
|
+
}), E("binds false and zero without treating them as omitted", async () => {
|
|
1013
1292
|
await t(async (e) => {
|
|
1014
1293
|
let t = {
|
|
1015
1294
|
id: "falsy",
|
|
@@ -1025,14 +1304,14 @@ var L = u("voltro_parity_composite", {
|
|
|
1025
1304
|
count: 0
|
|
1026
1305
|
}
|
|
1027
1306
|
});
|
|
1028
|
-
let n = await e.query(
|
|
1029
|
-
|
|
1307
|
+
let n = await e.query(M("voltro_parity_c"));
|
|
1308
|
+
T(n).toEqual([T.objectContaining({
|
|
1030
1309
|
id: "falsy",
|
|
1031
1310
|
title: "keep",
|
|
1032
1311
|
count: 0
|
|
1033
|
-
})]),
|
|
1312
|
+
})]), T([!1, 0]).toContain(n[0].flag);
|
|
1034
1313
|
});
|
|
1035
|
-
}),
|
|
1314
|
+
}), E("encodes JSON/array assignments and binds SQL-looking strings as data", async () => {
|
|
1036
1315
|
await t(async (e) => {
|
|
1037
1316
|
let t = {
|
|
1038
1317
|
id: "structured",
|
|
@@ -1057,7 +1336,7 @@ var L = u("voltro_parity_composite", {
|
|
|
1057
1336
|
update: [],
|
|
1058
1337
|
updateValues: n
|
|
1059
1338
|
};
|
|
1060
|
-
await e.upsert("voltro_parity_values", t, r),
|
|
1339
|
+
await e.upsert("voltro_parity_values", t, r), T(await e.query(M("voltro_parity_values"))).toEqual([t]), await e.upsert("voltro_parity_values", t, r), T(await e.query(M("voltro_parity_values"))).toEqual([{
|
|
1061
1340
|
id: "structured",
|
|
1062
1341
|
...n
|
|
1063
1342
|
}]), await e.upsert("voltro_parity_values", t, {
|
|
@@ -1066,14 +1345,14 @@ var L = u("voltro_parity_composite", {
|
|
|
1066
1345
|
metadata: null,
|
|
1067
1346
|
labels: []
|
|
1068
1347
|
}
|
|
1069
|
-
}),
|
|
1348
|
+
}), T(await e.query(M("voltro_parity_values"))).toEqual([{
|
|
1070
1349
|
id: "structured",
|
|
1071
1350
|
title: n.title,
|
|
1072
1351
|
metadata: null,
|
|
1073
1352
|
labels: []
|
|
1074
1353
|
}]);
|
|
1075
1354
|
});
|
|
1076
|
-
}),
|
|
1355
|
+
}), E("keeps structured raw-driver values working through all write verbs", async () => {
|
|
1077
1356
|
await t(async (e) => {
|
|
1078
1357
|
let t = {
|
|
1079
1358
|
title: "before",
|
|
@@ -1106,8 +1385,8 @@ var L = u("voltro_parity_composite", {
|
|
|
1106
1385
|
id: "ignore-new",
|
|
1107
1386
|
...n
|
|
1108
1387
|
}, { conflictColumns: ["id"] });
|
|
1109
|
-
let r = await e.query(
|
|
1110
|
-
|
|
1388
|
+
let r = await e.query(M("voltro_parity_values"));
|
|
1389
|
+
T([...r].sort((e, t) => String(e.id).localeCompare(String(t.id)))).toEqual([
|
|
1111
1390
|
{
|
|
1112
1391
|
id: "bulk",
|
|
1113
1392
|
...n
|
|
@@ -1126,7 +1405,7 @@ var L = u("voltro_parity_composite", {
|
|
|
1126
1405
|
}
|
|
1127
1406
|
]);
|
|
1128
1407
|
});
|
|
1129
|
-
}),
|
|
1408
|
+
}), E.each([
|
|
1130
1409
|
"id",
|
|
1131
1410
|
"teamId",
|
|
1132
1411
|
"year"
|
|
@@ -1142,20 +1421,20 @@ var L = u("voltro_parity_composite", {
|
|
|
1142
1421
|
update: [],
|
|
1143
1422
|
updateValues: { [e]: "different" }
|
|
1144
1423
|
};
|
|
1145
|
-
await
|
|
1424
|
+
await T(t.upsert("voltro_parity_upsert", n, r)).rejects.toThrow("identity"), T(await t.query(M("voltro_parity_upsert"))).toEqual([]), await t.insert("voltro_parity_upsert", n), await T(t.upsert("voltro_parity_upsert", n, r)).rejects.toThrow("identity"), T(await t.query(M("voltro_parity_upsert"))).toEqual([T.objectContaining(n)]);
|
|
1146
1425
|
});
|
|
1147
1426
|
});
|
|
1148
|
-
}),
|
|
1149
|
-
await
|
|
1427
|
+
}), E("upsert updateValues rolls back row changes and discards transaction events", async () => {
|
|
1428
|
+
await r.store.insert("voltro_parity_a", {
|
|
1150
1429
|
id: "rollback-values",
|
|
1151
1430
|
title: "keep",
|
|
1152
1431
|
count: 1
|
|
1153
1432
|
});
|
|
1154
|
-
let e = [], t =
|
|
1433
|
+
let e = [], t = r.store.onChange((t) => {
|
|
1155
1434
|
e.push(t);
|
|
1156
1435
|
});
|
|
1157
1436
|
try {
|
|
1158
|
-
await
|
|
1437
|
+
await T(r.store.transactional(async (e) => {
|
|
1159
1438
|
let t = await e.upsert("voltro_parity_a", {
|
|
1160
1439
|
id: "rollback-values",
|
|
1161
1440
|
title: "incoming",
|
|
@@ -1165,25 +1444,25 @@ var L = u("voltro_parity_composite", {
|
|
|
1165
1444
|
update: [],
|
|
1166
1445
|
updateValues: { count: 42 }
|
|
1167
1446
|
});
|
|
1168
|
-
throw
|
|
1447
|
+
throw T(t).toMatchObject({
|
|
1169
1448
|
title: "keep",
|
|
1170
1449
|
count: 42
|
|
1171
1450
|
}), Error("rollback values");
|
|
1172
|
-
})).rejects.toThrow("rollback values"),
|
|
1451
|
+
})).rejects.toThrow("rollback values"), T(await r.store.query(M("voltro_parity_a"))).toEqual([T.objectContaining({
|
|
1173
1452
|
id: "rollback-values",
|
|
1174
1453
|
title: "keep",
|
|
1175
1454
|
count: 1
|
|
1176
|
-
})]),
|
|
1455
|
+
})]), T(e).toEqual([]);
|
|
1177
1456
|
} finally {
|
|
1178
1457
|
t();
|
|
1179
1458
|
}
|
|
1180
|
-
}),
|
|
1181
|
-
await
|
|
1459
|
+
}), E("and a DIFFERENT row is still not silently overwritten", async () => {
|
|
1460
|
+
await r.store.upsert("voltro_parity_upsert", {
|
|
1182
1461
|
id: "up_a",
|
|
1183
1462
|
teamId: "t-1",
|
|
1184
1463
|
year: 2026,
|
|
1185
1464
|
hours: 1
|
|
1186
|
-
}, { conflictColumns: ["teamId", "year"] }), await
|
|
1465
|
+
}, { conflictColumns: ["teamId", "year"] }), await r.store.upsert("voltro_parity_upsert", {
|
|
1187
1466
|
id: "up_b",
|
|
1188
1467
|
teamId: "t-2",
|
|
1189
1468
|
year: 2026,
|
|
@@ -1192,66 +1471,66 @@ var L = u("voltro_parity_composite", {
|
|
|
1192
1471
|
conflictColumns: ["teamId", "year"],
|
|
1193
1472
|
update: ["hours"]
|
|
1194
1473
|
});
|
|
1195
|
-
let e = await
|
|
1196
|
-
|
|
1197
|
-
}),
|
|
1198
|
-
let e = await
|
|
1474
|
+
let e = await r.store.query(M("voltro_parity_upsert"));
|
|
1475
|
+
T(e.length).toBe(2), T([...e].map((e) => e.hours).sort()).toEqual([1, 2]);
|
|
1476
|
+
}), E("insert + update bind an explicit NULL into a datetime2/int column", async () => {
|
|
1477
|
+
let e = await r.store.insert("voltro_parity_d", {
|
|
1199
1478
|
id: "nd1",
|
|
1200
1479
|
title: "nulls",
|
|
1201
1480
|
at: null,
|
|
1202
1481
|
n: null
|
|
1203
1482
|
});
|
|
1204
|
-
|
|
1483
|
+
T(e).toMatchObject({
|
|
1205
1484
|
id: "nd1",
|
|
1206
1485
|
title: "nulls",
|
|
1207
1486
|
at: null,
|
|
1208
1487
|
n: null
|
|
1209
|
-
}), await
|
|
1488
|
+
}), await r.store.insert("voltro_parity_d", {
|
|
1210
1489
|
id: "nd2",
|
|
1211
1490
|
title: "set",
|
|
1212
1491
|
at: /* @__PURE__ */ new Date(),
|
|
1213
1492
|
n: 7
|
|
1214
1493
|
});
|
|
1215
|
-
let t = await
|
|
1494
|
+
let t = await r.store.update("voltro_parity_d", "nd2", {
|
|
1216
1495
|
at: null,
|
|
1217
1496
|
n: null
|
|
1218
1497
|
});
|
|
1219
|
-
|
|
1498
|
+
T(t).toMatchObject({
|
|
1220
1499
|
id: "nd2",
|
|
1221
1500
|
at: null,
|
|
1222
1501
|
n: null
|
|
1223
1502
|
});
|
|
1224
|
-
}),
|
|
1225
|
-
await
|
|
1503
|
+
}), E("updateMany updates only the rows matching the predicate + returns the count", async () => {
|
|
1504
|
+
await r.store.insert("voltro_parity_a", {
|
|
1226
1505
|
id: "m1",
|
|
1227
1506
|
title: "x",
|
|
1228
1507
|
count: 5
|
|
1229
|
-
}), await
|
|
1508
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1230
1509
|
id: "m2",
|
|
1231
1510
|
title: "y",
|
|
1232
1511
|
count: 5
|
|
1233
|
-
}), await
|
|
1512
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1234
1513
|
id: "m3",
|
|
1235
1514
|
title: "z",
|
|
1236
1515
|
count: 9
|
|
1237
1516
|
});
|
|
1238
|
-
let e = await
|
|
1517
|
+
let e = await r.store.updateMany("voltro_parity_a", { title: "hit" }, { where: {
|
|
1239
1518
|
column: "count",
|
|
1240
1519
|
op: "eq",
|
|
1241
1520
|
value: 5
|
|
1242
1521
|
} });
|
|
1243
|
-
|
|
1244
|
-
let t = await
|
|
1245
|
-
...
|
|
1522
|
+
T(e).toBe(2);
|
|
1523
|
+
let t = await r.store.query({
|
|
1524
|
+
...M("voltro_parity_a"),
|
|
1246
1525
|
predicate: {
|
|
1247
1526
|
column: "title",
|
|
1248
1527
|
op: "eq",
|
|
1249
1528
|
value: "hit"
|
|
1250
1529
|
}
|
|
1251
1530
|
});
|
|
1252
|
-
|
|
1253
|
-
}),
|
|
1254
|
-
await
|
|
1531
|
+
T(t.length).toBe(2);
|
|
1532
|
+
}), E("updateMany with an AND predicate is an atomic compare-and-set (the wakeup-claim pattern)", async () => {
|
|
1533
|
+
await r.store.insert("voltro_parity_a", {
|
|
1255
1534
|
id: "cas",
|
|
1256
1535
|
title: "pending",
|
|
1257
1536
|
count: 1
|
|
@@ -1264,76 +1543,76 @@ var L = u("voltro_parity_composite", {
|
|
|
1264
1543
|
column: "title",
|
|
1265
1544
|
op: "eq",
|
|
1266
1545
|
value: e
|
|
1267
|
-
}] }), t = await
|
|
1268
|
-
|
|
1269
|
-
let
|
|
1270
|
-
|
|
1271
|
-
}),
|
|
1272
|
-
await
|
|
1546
|
+
}] }), t = await r.store.updateMany("voltro_parity_a", { title: "claimed" }, { where: e("pending") });
|
|
1547
|
+
T(t).toBe(1);
|
|
1548
|
+
let n = await r.store.updateMany("voltro_parity_a", { title: "claimed-again" }, { where: e("pending") });
|
|
1549
|
+
T(n).toBe(0);
|
|
1550
|
+
}), E("deleteMany removes only the rows matching the predicate + returns the count", async () => {
|
|
1551
|
+
await r.store.insert("voltro_parity_a", {
|
|
1273
1552
|
id: "d1",
|
|
1274
1553
|
title: "x",
|
|
1275
1554
|
count: 5
|
|
1276
|
-
}), await
|
|
1555
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1277
1556
|
id: "d2",
|
|
1278
1557
|
title: "y",
|
|
1279
1558
|
count: 5
|
|
1280
|
-
}), await
|
|
1559
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1281
1560
|
id: "d3",
|
|
1282
1561
|
title: "z",
|
|
1283
1562
|
count: 9
|
|
1284
1563
|
});
|
|
1285
|
-
let e = await
|
|
1564
|
+
let e = await r.store.deleteMany("voltro_parity_a", { where: {
|
|
1286
1565
|
column: "count",
|
|
1287
1566
|
op: "eq",
|
|
1288
1567
|
value: 5
|
|
1289
1568
|
} });
|
|
1290
|
-
|
|
1291
|
-
let t = await
|
|
1292
|
-
|
|
1293
|
-
}),
|
|
1294
|
-
await
|
|
1569
|
+
T(e).toBe(2);
|
|
1570
|
+
let t = await r.store.query(M("voltro_parity_a"));
|
|
1571
|
+
T(t.map((e) => e.id)).toEqual(["d3"]);
|
|
1572
|
+
}), E("deleteMany emits one delete ChangeEvent per removed row (old-image)", async () => {
|
|
1573
|
+
await r.store.insert("voltro_parity_a", {
|
|
1295
1574
|
id: "e1",
|
|
1296
1575
|
title: "gone",
|
|
1297
1576
|
count: 1
|
|
1298
|
-
}), await
|
|
1577
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1299
1578
|
id: "e2",
|
|
1300
1579
|
title: "gone",
|
|
1301
1580
|
count: 1
|
|
1302
|
-
}), await
|
|
1581
|
+
}), await r.store.insert("voltro_parity_a", {
|
|
1303
1582
|
id: "e3",
|
|
1304
1583
|
title: "stay",
|
|
1305
1584
|
count: 2
|
|
1306
1585
|
});
|
|
1307
|
-
let e = [], t =
|
|
1586
|
+
let e = [], t = r.store.onChange((t) => e.push({
|
|
1308
1587
|
op: t.op,
|
|
1309
1588
|
id: t.old?.id
|
|
1310
|
-
})),
|
|
1589
|
+
})), n = await r.store.deleteMany("voltro_parity_a", { where: {
|
|
1311
1590
|
column: "title",
|
|
1312
1591
|
op: "eq",
|
|
1313
1592
|
value: "gone"
|
|
1314
1593
|
} });
|
|
1315
|
-
t(),
|
|
1594
|
+
t(), T(n).toBe(2);
|
|
1316
1595
|
let i = e.filter((e) => e.op === "delete");
|
|
1317
|
-
|
|
1318
|
-
}),
|
|
1319
|
-
await
|
|
1596
|
+
T(i).toHaveLength(2), T(i.map((e) => e.id).sort()).toEqual(["e1", "e2"]);
|
|
1597
|
+
}), E("deleteMany with no match returns 0 and removes nothing", async () => {
|
|
1598
|
+
await r.store.insert("voltro_parity_a", {
|
|
1320
1599
|
id: "k1",
|
|
1321
1600
|
title: "keep",
|
|
1322
1601
|
count: 1
|
|
1323
1602
|
});
|
|
1324
|
-
let e = await
|
|
1603
|
+
let e = await r.store.deleteMany("voltro_parity_a", { where: {
|
|
1325
1604
|
column: "title",
|
|
1326
1605
|
op: "eq",
|
|
1327
1606
|
value: "absent"
|
|
1328
1607
|
} });
|
|
1329
|
-
|
|
1330
|
-
let t = await
|
|
1331
|
-
|
|
1332
|
-
}),
|
|
1333
|
-
let e =
|
|
1334
|
-
|
|
1608
|
+
T(e).toBe(0);
|
|
1609
|
+
let t = await r.store.query(M("voltro_parity_a"));
|
|
1610
|
+
T(t.length).toBe(1);
|
|
1611
|
+
}), E("retryFilter recognises the dialect's own transient codes", () => {
|
|
1612
|
+
let e = n.dialect.retryFilter(/* @__PURE__ */ Error("garbage"));
|
|
1613
|
+
T(["retry", "noRetry"]).toContain(e);
|
|
1335
1614
|
});
|
|
1336
1615
|
});
|
|
1337
1616
|
};
|
|
1338
1617
|
//#endregion
|
|
1339
|
-
export {
|
|
1618
|
+
export { k as applySchema, U as runDialectParity };
|