@voltro/testing 0.65.0 → 0.66.1
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 +102 -0
- package/dist/dialect.js +941 -235
- package/dist/index.d.ts +10 -0
- package/dist/index.js +136 -119
- package/package.json +8 -8
package/dist/dialect.js
CHANGED
|
@@ -1,61 +1,81 @@
|
|
|
1
|
-
import { _voltroMigrationPlansTable as e,
|
|
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, table as u, text as d, timestamp as f } from "@voltro/database";
|
|
2
|
+
import { Effect as p } from "effect";
|
|
3
|
+
import { _voltroUndoLogTable as m, applyInverses as h, makeSchemaRegistry as g, synthesizeInverse as _, wrapStoreWithMixinBehaviour as v } from "@voltro/runtime";
|
|
4
|
+
import { afterEach as y, beforeEach as b, describe as x, expect as S, it as C } from "vitest";
|
|
5
|
+
import { SqlClient as w } from "@effect/sql";
|
|
6
|
+
import { applyPlan as T, applySchema as E, introspectSchema as D, planMigrations as O } from "@voltro/database/sql";
|
|
7
7
|
//#region src/dialectParity.ts
|
|
8
|
-
var
|
|
8
|
+
var k = (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
|
-
}),
|
|
16
|
-
id:
|
|
17
|
-
title:
|
|
18
|
-
count:
|
|
19
|
-
}),
|
|
20
|
-
id:
|
|
21
|
-
title:
|
|
22
|
-
}),
|
|
23
|
-
id:
|
|
24
|
-
title:
|
|
25
|
-
count:
|
|
26
|
-
flag:
|
|
27
|
-
}),
|
|
28
|
-
id:
|
|
29
|
-
title:
|
|
30
|
-
at:
|
|
31
|
-
n:
|
|
32
|
-
}),
|
|
33
|
-
id:
|
|
34
|
-
teamId:
|
|
35
|
-
year:
|
|
36
|
-
hours:
|
|
37
|
-
}).unique("byTeamYear", ["teamId", "year"]),
|
|
38
|
-
id:
|
|
39
|
-
title:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
43
|
-
|
|
15
|
+
}), A = u("voltro_parity_a", {
|
|
16
|
+
id: o(),
|
|
17
|
+
title: d(),
|
|
18
|
+
count: s()
|
|
19
|
+
}), j = u("voltro_parity_b", {
|
|
20
|
+
id: o(),
|
|
21
|
+
title: d()
|
|
22
|
+
}), M = u("voltro_parity_c", {
|
|
23
|
+
id: o(),
|
|
24
|
+
title: d(),
|
|
25
|
+
count: s(),
|
|
26
|
+
flag: n().default(!1)
|
|
27
|
+
}), N = u("voltro_parity_d", {
|
|
28
|
+
id: o(),
|
|
29
|
+
title: d(),
|
|
30
|
+
at: f().nullable().onUpdate("now"),
|
|
31
|
+
n: s().nullable()
|
|
32
|
+
}), P = u("voltro_parity_upsert", {
|
|
33
|
+
id: o(),
|
|
34
|
+
teamId: d(),
|
|
35
|
+
year: s(),
|
|
36
|
+
hours: s().nullable()
|
|
37
|
+
}).unique("byTeamYear", ["teamId", "year"]), F = u("voltro_parity_values", {
|
|
38
|
+
id: o(),
|
|
39
|
+
title: d().onUpdate(() => "automatic"),
|
|
40
|
+
metadata: c().nullable(),
|
|
41
|
+
labels: t(d()).nullable()
|
|
42
|
+
});
|
|
43
|
+
l(F);
|
|
44
|
+
var I = u("voltro_parity_json_names", {
|
|
45
|
+
id: o(),
|
|
46
|
+
key: c(),
|
|
47
|
+
value: c()
|
|
48
|
+
});
|
|
49
|
+
l(I);
|
|
50
|
+
var L = u("voltro_parity_composite", {
|
|
51
|
+
userId: d(),
|
|
52
|
+
orgId: d(),
|
|
53
|
+
role: d()
|
|
54
|
+
}).primaryKey(["userId", "orgId"]), R = u("voltro_parity_bulk", {
|
|
55
|
+
id: o(),
|
|
56
|
+
title: d(),
|
|
57
|
+
count: s(),
|
|
58
|
+
flag: n().default(!1)
|
|
59
|
+
}), z = (e) => {
|
|
60
|
+
let t = r(e);
|
|
44
61
|
if (t === void 0) return 0;
|
|
45
|
-
let
|
|
46
|
-
return Math.max(1, Math.min(Math.floor(t.maxBindParameters /
|
|
47
|
-
},
|
|
48
|
-
|
|
62
|
+
let n = Object.keys(R.fields).length;
|
|
63
|
+
return Math.max(1, Math.min(Math.floor(t.maxBindParameters / n), t.maxRowsPerStatement ?? Infinity));
|
|
64
|
+
}, B = (t) => {
|
|
65
|
+
x(`dialect parity: ${t.name ?? t.dialect.id}`, () => {
|
|
49
66
|
let n = null;
|
|
50
|
-
|
|
67
|
+
b(async () => {
|
|
51
68
|
await t.setup(), n = await t.make(), await n.migrate([
|
|
52
|
-
T,
|
|
53
|
-
E,
|
|
54
|
-
D,
|
|
55
|
-
O,
|
|
56
69
|
A,
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
j,
|
|
71
|
+
M,
|
|
72
|
+
N,
|
|
73
|
+
R,
|
|
74
|
+
P,
|
|
75
|
+
L,
|
|
76
|
+
F,
|
|
77
|
+
I,
|
|
78
|
+
m
|
|
59
79
|
]);
|
|
60
80
|
for (let e of [
|
|
61
81
|
"voltro_parity_a",
|
|
@@ -63,151 +83,157 @@ var w = (e) => ({
|
|
|
63
83
|
"voltro_parity_c",
|
|
64
84
|
"voltro_parity_d",
|
|
65
85
|
"voltro_parity_upsert",
|
|
86
|
+
"voltro_parity_values",
|
|
87
|
+
"voltro_parity_json_names",
|
|
66
88
|
"_voltro_undo_log"
|
|
67
89
|
]) {
|
|
68
|
-
let t = await n.store.query(
|
|
90
|
+
let t = await n.store.query(k(e));
|
|
69
91
|
for (let r of t) await n.store.delete(e, r.id);
|
|
70
92
|
}
|
|
71
|
-
|
|
93
|
+
for (let e of await n.store.query(k("voltro_parity_composite"))) await n.store.delete("voltro_parity_composite", {
|
|
94
|
+
userId: e.userId,
|
|
95
|
+
orgId: e.orgId
|
|
96
|
+
});
|
|
97
|
+
}), y(async () => {
|
|
72
98
|
await n?.dispose(), await t.teardown(), n = null;
|
|
73
|
-
}),
|
|
74
|
-
let r = "voltro_rename_src", i = "_voltro_rename_dst",
|
|
75
|
-
await
|
|
76
|
-
let e = yield*
|
|
77
|
-
for (let t of [i, r]) yield*
|
|
99
|
+
}), C("rename-table: the rows move, and the plan converges", async () => {
|
|
100
|
+
let r = "voltro_rename_src", i = "_voltro_rename_dst", a = n.run, s = async () => {
|
|
101
|
+
await a(p.gen(function* () {
|
|
102
|
+
let e = yield* w.SqlClient;
|
|
103
|
+
for (let t of [i, r]) yield* p.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${t}`), () => []);
|
|
78
104
|
})).catch(() => {});
|
|
79
|
-
},
|
|
80
|
-
let e = yield*
|
|
105
|
+
}, c = () => a(p.gen(function* () {
|
|
106
|
+
let e = yield* w.SqlClient, t = yield* D(e);
|
|
81
107
|
return {
|
|
82
108
|
...t,
|
|
83
109
|
tables: t.tables.filter((e) => e.name === r || e.name === i)
|
|
84
110
|
};
|
|
85
|
-
})),
|
|
111
|
+
})), l = async () => O({
|
|
86
112
|
declared: [m],
|
|
87
|
-
live: await
|
|
113
|
+
live: await c(),
|
|
88
114
|
dialect: t.dialect.id
|
|
89
115
|
});
|
|
90
|
-
await
|
|
91
|
-
let
|
|
92
|
-
id:
|
|
93
|
-
title:
|
|
94
|
-
}), m =
|
|
95
|
-
id:
|
|
96
|
-
title:
|
|
116
|
+
await s();
|
|
117
|
+
let f = u(r, {
|
|
118
|
+
id: o({ prefix: "rn" }),
|
|
119
|
+
title: d()
|
|
120
|
+
}), m = u(i, {
|
|
121
|
+
id: o({ prefix: "rn" }),
|
|
122
|
+
title: d()
|
|
97
123
|
}).renamedFrom(r);
|
|
98
|
-
await n.migrate([
|
|
124
|
+
await n.migrate([f, e]), await n.store.insert(r, {
|
|
99
125
|
id: "rn_1",
|
|
100
126
|
title: "one"
|
|
101
127
|
}), await n.store.insert(r, {
|
|
102
128
|
id: "rn_2",
|
|
103
129
|
title: "two"
|
|
104
130
|
});
|
|
105
|
-
let h = await
|
|
106
|
-
|
|
107
|
-
let e = yield*
|
|
108
|
-
return yield*
|
|
131
|
+
let h = await l();
|
|
132
|
+
S(h.operations.some((e) => e.op.kind === "rename-table"), "the marker must fold").toBe(!0), S(h.operations.filter((e) => e.blocked !== void 0), "nothing may be blocked").toEqual([]), await a(p.gen(function* () {
|
|
133
|
+
let e = yield* w.SqlClient;
|
|
134
|
+
return yield* T(e, h, {
|
|
109
135
|
declared: [m],
|
|
110
136
|
appliedBy: "dialect-parity",
|
|
111
137
|
environment: "dev",
|
|
112
138
|
source: "auto-diff",
|
|
113
|
-
replan: () =>
|
|
139
|
+
replan: () => p.promise(l)
|
|
114
140
|
});
|
|
115
141
|
}));
|
|
116
|
-
let g = await n.store.query(
|
|
117
|
-
|
|
118
|
-
}),
|
|
119
|
-
let
|
|
120
|
-
await
|
|
121
|
-
let e = yield*
|
|
122
|
-
yield*
|
|
142
|
+
let g = await n.store.query(k(i));
|
|
143
|
+
S(g.map((e) => e.id).sort(), "every row survived the rename").toEqual(["rn_1", "rn_2"]), S((await l()).operations, `${t.dialect.id}: the plan must converge`).toEqual([]), await s();
|
|
144
|
+
}), C("schema evolution: create → add column → add index → drop column, converging at each step", async () => {
|
|
145
|
+
let r = "voltro_parity_evolve", a = n.run, c = async () => {
|
|
146
|
+
await a(p.gen(function* () {
|
|
147
|
+
let e = yield* w.SqlClient;
|
|
148
|
+
yield* p.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${r}`), () => []);
|
|
123
149
|
})).catch(() => {});
|
|
124
|
-
},
|
|
125
|
-
let e = yield*
|
|
150
|
+
}, l = () => a(p.gen(function* () {
|
|
151
|
+
let e = yield* w.SqlClient, t = yield* D(e);
|
|
126
152
|
return {
|
|
127
153
|
...t,
|
|
128
|
-
tables: t.tables.filter((e) => e.name ===
|
|
154
|
+
tables: t.tables.filter((e) => e.name === r)
|
|
129
155
|
};
|
|
130
|
-
})),
|
|
156
|
+
})), f = async (e) => O({
|
|
131
157
|
declared: e,
|
|
132
|
-
live: await
|
|
158
|
+
live: await l(),
|
|
133
159
|
dialect: t.dialect.id
|
|
134
|
-
}), m = async () => (await
|
|
135
|
-
let
|
|
136
|
-
n &&
|
|
137
|
-
let n = yield*
|
|
138
|
-
return yield*
|
|
160
|
+
}), m = async () => (await l()).tables[0]?.columns.map((e) => e.name) ?? [], h = async (e, n) => {
|
|
161
|
+
let i = await f(e);
|
|
162
|
+
n && S(i.operations.length, `${t.dialect.id}: nothing to apply — the step asserts nothing`).toBeGreaterThan(0), S(i.operations.filter((e) => e.blocked !== void 0), `${t.dialect.id}: nothing may be blocked`).toEqual([]), i.operations.length !== 0 && (await a(p.gen(function* () {
|
|
163
|
+
let n = yield* w.SqlClient;
|
|
164
|
+
return yield* T(n, i, {
|
|
139
165
|
declared: e,
|
|
140
166
|
appliedBy: "dialect-parity",
|
|
141
167
|
environment: "dev",
|
|
142
168
|
source: "auto-diff",
|
|
143
|
-
replan: (n) =>
|
|
169
|
+
replan: (n) => p.map(D(n), (n) => O({
|
|
144
170
|
declared: e,
|
|
145
171
|
live: {
|
|
146
172
|
...n,
|
|
147
|
-
tables: n.tables.filter((e) => e.name ===
|
|
173
|
+
tables: n.tables.filter((e) => e.name === r)
|
|
148
174
|
},
|
|
149
175
|
dialect: t.dialect.id
|
|
150
176
|
}))
|
|
151
177
|
});
|
|
152
|
-
})),
|
|
178
|
+
})), S((await f(e)).operations, `${t.dialect.id}: the plan must converge`).toEqual([]));
|
|
153
179
|
};
|
|
154
|
-
await
|
|
155
|
-
id:
|
|
156
|
-
name:
|
|
157
|
-
})], !0),
|
|
180
|
+
await c(), await n.migrate([e]), await h([u(r, {
|
|
181
|
+
id: o({ prefix: "ev" }),
|
|
182
|
+
name: d()
|
|
183
|
+
})], !0), S(await m(), `${t.dialect.id}: v1 columns`).toEqual(["id", "name"]), await n.store.insert(r, {
|
|
158
184
|
id: "ev_1",
|
|
159
185
|
name: "one"
|
|
160
|
-
}), await n.store.insert(
|
|
186
|
+
}), await n.store.insert(r, {
|
|
161
187
|
id: "ev_2",
|
|
162
188
|
name: "two"
|
|
163
|
-
}), await h([
|
|
164
|
-
id:
|
|
165
|
-
name:
|
|
166
|
-
qty:
|
|
167
|
-
})], !0),
|
|
168
|
-
id:
|
|
169
|
-
name:
|
|
170
|
-
qty:
|
|
171
|
-
}).index(["qty"])], !0), await h([
|
|
172
|
-
id:
|
|
173
|
-
name:
|
|
174
|
-
qty:
|
|
175
|
-
})], !0),
|
|
176
|
-
let g = await n.store.query(
|
|
177
|
-
|
|
178
|
-
}),
|
|
179
|
-
let r = "voltro_parity_reapply", i = n.run,
|
|
180
|
-
await i(
|
|
181
|
-
let e = yield*
|
|
182
|
-
yield*
|
|
189
|
+
}), await h([u(r, {
|
|
190
|
+
id: o({ prefix: "ev" }),
|
|
191
|
+
name: d(),
|
|
192
|
+
qty: s().nullable()
|
|
193
|
+
})], !0), S(await m()).toContain("qty"), await h([u(r, {
|
|
194
|
+
id: o({ prefix: "ev" }),
|
|
195
|
+
name: d(),
|
|
196
|
+
qty: s().nullable()
|
|
197
|
+
}).index(["qty"])], !0), await h([u(r, {
|
|
198
|
+
id: o({ prefix: "ev" }),
|
|
199
|
+
name: d(),
|
|
200
|
+
qty: i()
|
|
201
|
+
})], !0), S(await m(), `${t.dialect.id}: v4 columns`).toEqual(["id", "name"]);
|
|
202
|
+
let g = await n.store.query(k(r));
|
|
203
|
+
S(g.map((e) => e.id).sort(), `${t.dialect.id}: rows survived the evolution`).toEqual(["ev_1", "ev_2"]), await c();
|
|
204
|
+
}), C("re-applying a create-table plan is a no-op, indexes included", async () => {
|
|
205
|
+
let r = "voltro_parity_reapply", i = n.run, a = async () => {
|
|
206
|
+
await i(p.gen(function* () {
|
|
207
|
+
let e = yield* w.SqlClient;
|
|
208
|
+
yield* p.orElseSucceed(e.unsafe(`DROP TABLE IF EXISTS ${r}`), () => []);
|
|
183
209
|
})).catch(() => {});
|
|
184
|
-
},
|
|
185
|
-
let e = yield*
|
|
210
|
+
}, c = () => i(p.gen(function* () {
|
|
211
|
+
let e = yield* w.SqlClient, t = yield* D(e);
|
|
186
212
|
return {
|
|
187
213
|
...t,
|
|
188
214
|
tables: t.tables.filter((e) => e.name === r)
|
|
189
215
|
};
|
|
190
|
-
})),
|
|
191
|
-
id:
|
|
192
|
-
name:
|
|
193
|
-
qty:
|
|
194
|
-
}).index(["qty"]),
|
|
195
|
-
declared: [
|
|
196
|
-
live: await
|
|
216
|
+
})), l = u(r, {
|
|
217
|
+
id: o({ prefix: "ra" }),
|
|
218
|
+
name: d(),
|
|
219
|
+
qty: s().nullable()
|
|
220
|
+
}).index(["qty"]), f = async () => O({
|
|
221
|
+
declared: [l],
|
|
222
|
+
live: await c(),
|
|
197
223
|
dialect: t.dialect.id
|
|
198
224
|
});
|
|
199
|
-
await
|
|
200
|
-
let m = await
|
|
201
|
-
|
|
202
|
-
for (let e = 0; e < 2; e++) await i(
|
|
203
|
-
let e = yield*
|
|
204
|
-
return yield*
|
|
205
|
-
declared: [
|
|
225
|
+
await a(), await n.migrate([e]);
|
|
226
|
+
let m = await f();
|
|
227
|
+
S(m.operations.some((e) => e.op.kind === "create-table"), `${t.dialect.id}: expected a create-table to re-apply`).toBe(!0);
|
|
228
|
+
for (let e = 0; e < 2; e++) await i(p.gen(function* () {
|
|
229
|
+
let e = yield* w.SqlClient;
|
|
230
|
+
return yield* T(e, m, {
|
|
231
|
+
declared: [l],
|
|
206
232
|
appliedBy: "dialect-parity",
|
|
207
233
|
environment: "dev",
|
|
208
234
|
source: "auto-diff",
|
|
209
|
-
replan: (e) =>
|
|
210
|
-
declared: [
|
|
235
|
+
replan: (e) => p.map(D(e), (e) => O({
|
|
236
|
+
declared: [l],
|
|
211
237
|
live: {
|
|
212
238
|
...e,
|
|
213
239
|
tables: e.tables.filter((e) => e.name === r)
|
|
@@ -216,71 +242,115 @@ var w = (e) => ({
|
|
|
216
242
|
}))
|
|
217
243
|
});
|
|
218
244
|
}));
|
|
219
|
-
|
|
245
|
+
S((await f()).operations, `${t.dialect.id}: the re-applied plan must converge`).toEqual([]), await a();
|
|
220
246
|
});
|
|
221
|
-
let
|
|
222
|
-
let e = yield*
|
|
223
|
-
yield*
|
|
224
|
-
})),
|
|
247
|
+
let r = "voltro_parity_bulk", c = () => n.run(p.gen(function* () {
|
|
248
|
+
let e = yield* w.SqlClient;
|
|
249
|
+
yield* p.orElseSucceed(e.unsafe(`DELETE FROM ${r}`), () => []);
|
|
250
|
+
})), l = (e, t) => Array.from({ length: e }, (e, n) => ({
|
|
225
251
|
id: `${t}_${String(n).padStart(6, "0")}`,
|
|
226
252
|
title: `row ${n}`,
|
|
227
253
|
count: n,
|
|
228
254
|
flag: !1
|
|
229
255
|
}));
|
|
230
|
-
|
|
231
|
-
let e =
|
|
232
|
-
|
|
233
|
-
let
|
|
234
|
-
await
|
|
235
|
-
let
|
|
236
|
-
|
|
237
|
-
let
|
|
238
|
-
|
|
239
|
-
}, 12e4),
|
|
240
|
-
let e =
|
|
241
|
-
await
|
|
242
|
-
let
|
|
243
|
-
|
|
244
|
-
...
|
|
245
|
-
id:
|
|
246
|
-
}, await
|
|
247
|
-
let
|
|
248
|
-
|
|
249
|
-
}, 12e4),
|
|
250
|
-
await n.migrate([
|
|
251
|
-
}),
|
|
256
|
+
C("insertMany past this dialect's one-statement ceiling lands EVERY row", async () => {
|
|
257
|
+
let e = z(t.dialect.id);
|
|
258
|
+
S(e, `no bulk-insert limits are declared for '${t.dialect.id}' — a dialect with no entry chunks at nothing, so this scenario would pass having tested nothing`).toBeGreaterThan(0);
|
|
259
|
+
let i = e + 5;
|
|
260
|
+
await c();
|
|
261
|
+
let a = await n.store.insertMany(r, l(i, "bulk"));
|
|
262
|
+
S(a.length, `insertMany returned ${a.length} of ${i} post-images`).toBe(i), S(a[0]?.id).toBe("bulk_000000"), S(a[i - 1]?.id).toBe(`bulk_${String(i - 1).padStart(6, "0")}`);
|
|
263
|
+
let o = await n.store.query(k(r));
|
|
264
|
+
S(o.length, "the database holds fewer rows than insertMany reported").toBe(i), await c();
|
|
265
|
+
}, 12e4), C("a chunked insertMany is still ALL-OR-NOTHING", async () => {
|
|
266
|
+
let e = z(t.dialect.id) + 5;
|
|
267
|
+
await c();
|
|
268
|
+
let i = l(e, "atomic");
|
|
269
|
+
i[e - 1] = {
|
|
270
|
+
...i[e - 1],
|
|
271
|
+
id: i[0].id
|
|
272
|
+
}, await S(n.store.insertMany(r, i), "a duplicate primary key must still reject the whole call").rejects.toThrow();
|
|
273
|
+
let a = await n.store.query(k(r));
|
|
274
|
+
S(a.length, `${t.dialect.id}: ${a.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 c();
|
|
275
|
+
}, 12e4), C("DDL is idempotent — re-applying the schema is a no-op", async () => {
|
|
276
|
+
await n.migrate([A, j]);
|
|
277
|
+
}), C("insert + query round-trips a row", async () => {
|
|
252
278
|
let e = await n.store.insert("voltro_parity_a", {
|
|
253
279
|
id: "a1",
|
|
254
280
|
title: "one",
|
|
255
281
|
count: 1
|
|
256
282
|
});
|
|
257
|
-
|
|
283
|
+
S(e).toMatchObject({
|
|
258
284
|
id: "a1",
|
|
259
285
|
title: "one",
|
|
260
286
|
count: 1
|
|
261
287
|
});
|
|
262
|
-
let t = await n.store.query(
|
|
263
|
-
|
|
264
|
-
}),
|
|
288
|
+
let t = await n.store.query(k("voltro_parity_a"));
|
|
289
|
+
S(t.length).toBe(1), S(t[0]?.id).toBe("a1");
|
|
290
|
+
}), C("update returns the post-image", async () => {
|
|
265
291
|
await n.store.insert("voltro_parity_a", {
|
|
266
292
|
id: "a2",
|
|
267
293
|
title: "pre",
|
|
268
294
|
count: 1
|
|
269
295
|
});
|
|
270
296
|
let e = await n.store.update("voltro_parity_a", "a2", { title: "post" });
|
|
271
|
-
|
|
297
|
+
S(e).toMatchObject({
|
|
272
298
|
id: "a2",
|
|
273
299
|
title: "post"
|
|
274
300
|
});
|
|
275
|
-
}),
|
|
301
|
+
}), C("delete removes the row + reports true", async () => {
|
|
276
302
|
await n.store.insert("voltro_parity_a", {
|
|
277
303
|
id: "a3",
|
|
278
304
|
title: "x",
|
|
279
305
|
count: 1
|
|
280
|
-
}),
|
|
281
|
-
let e = await n.store.query(
|
|
282
|
-
|
|
283
|
-
}),
|
|
306
|
+
}), S(await n.store.delete("voltro_parity_a", "a3")).toBe(!0);
|
|
307
|
+
let e = await n.store.query(k("voltro_parity_a"));
|
|
308
|
+
S(e.length).toBe(0);
|
|
309
|
+
}), C("a composite key addresses the row, and only that row", async () => {
|
|
310
|
+
await n.store.insert("voltro_parity_composite", {
|
|
311
|
+
userId: "u1",
|
|
312
|
+
orgId: "o1",
|
|
313
|
+
role: "admin"
|
|
314
|
+
}), await n.store.insert("voltro_parity_composite", {
|
|
315
|
+
userId: "u1",
|
|
316
|
+
orgId: "o2",
|
|
317
|
+
role: "viewer"
|
|
318
|
+
});
|
|
319
|
+
let e = await n.store.update("voltro_parity_composite", {
|
|
320
|
+
userId: "u1",
|
|
321
|
+
orgId: "o2"
|
|
322
|
+
}, { role: "member" });
|
|
323
|
+
S(e).toMatchObject({
|
|
324
|
+
userId: "u1",
|
|
325
|
+
orgId: "o2",
|
|
326
|
+
role: "member"
|
|
327
|
+
});
|
|
328
|
+
let t = await n.store.query(k("voltro_parity_composite")), r = new Map(t.map((e) => [e.orgId, e.role]));
|
|
329
|
+
S(r.get("o1")).toBe("admin"), S(r.get("o2")).toBe("member");
|
|
330
|
+
}), C("a composite key that matches nothing is `null` / `false`, not an error", async () => {
|
|
331
|
+
S(await n.store.update("voltro_parity_composite", {
|
|
332
|
+
userId: "nobody",
|
|
333
|
+
orgId: "nowhere"
|
|
334
|
+
}, { role: "x" })).toBeNull(), S(await n.store.delete("voltro_parity_composite", {
|
|
335
|
+
userId: "nobody",
|
|
336
|
+
orgId: "nowhere"
|
|
337
|
+
})).toBe(!1);
|
|
338
|
+
}), C("a composite key deletes exactly the row it names", async () => {
|
|
339
|
+
await n.store.insert("voltro_parity_composite", {
|
|
340
|
+
userId: "u2",
|
|
341
|
+
orgId: "o1",
|
|
342
|
+
role: "admin"
|
|
343
|
+
}), await n.store.insert("voltro_parity_composite", {
|
|
344
|
+
userId: "u2",
|
|
345
|
+
orgId: "o2",
|
|
346
|
+
role: "viewer"
|
|
347
|
+
}), S(await n.store.delete("voltro_parity_composite", {
|
|
348
|
+
userId: "u2",
|
|
349
|
+
orgId: "o1"
|
|
350
|
+
})).toBe(!0);
|
|
351
|
+
let e = (await n.store.query(k("voltro_parity_composite"))).filter((e) => e.userId === "u2").map((e) => e.orgId);
|
|
352
|
+
S(e).toEqual(["o2"]);
|
|
353
|
+
}), C("undo: the _voltro_undo_log TEXT changes column round-trips a JSON ChangeSet", async () => {
|
|
284
354
|
let e = [{
|
|
285
355
|
table: "voltro_parity_c",
|
|
286
356
|
op: "update",
|
|
@@ -310,9 +380,9 @@ var w = (e) => ({
|
|
|
310
380
|
undone: !1,
|
|
311
381
|
createdAt: /* @__PURE__ */ new Date()
|
|
312
382
|
});
|
|
313
|
-
let t = await n.store.query(
|
|
314
|
-
|
|
315
|
-
}),
|
|
383
|
+
let t = await n.store.query(k("_voltro_undo_log"));
|
|
384
|
+
S(t.length).toBe(1), S(JSON.parse(t[0].changes)).toEqual(e);
|
|
385
|
+
}), C("undo: reverts an INSERT (inverse delete) on this dialect", async () => {
|
|
316
386
|
let e = {
|
|
317
387
|
invocationId: "inv1",
|
|
318
388
|
subject: null,
|
|
@@ -327,10 +397,10 @@ var w = (e) => ({
|
|
|
327
397
|
})
|
|
328
398
|
}]
|
|
329
399
|
};
|
|
330
|
-
await
|
|
331
|
-
let t = await n.store.query(
|
|
332
|
-
|
|
333
|
-
}),
|
|
400
|
+
await h(n.store, _(e));
|
|
401
|
+
let t = await n.store.query(k("voltro_parity_c"));
|
|
402
|
+
S(t.find((e) => e.id === "c2")).toBeUndefined();
|
|
403
|
+
}), C("undo: reverts an UPDATE including the BOOLEAN, restoring the captured value FAITHFULLY", async () => {
|
|
334
404
|
await n.store.insert("voltro_parity_c", {
|
|
335
405
|
id: "c3",
|
|
336
406
|
title: "t",
|
|
@@ -338,13 +408,13 @@ var w = (e) => ({
|
|
|
338
408
|
flag: !0
|
|
339
409
|
});
|
|
340
410
|
let e = (await n.store.query({
|
|
341
|
-
...
|
|
342
|
-
predicate:
|
|
411
|
+
...k("voltro_parity_c"),
|
|
412
|
+
predicate: a("id", "c3")
|
|
343
413
|
}))[0], t = await n.store.update("voltro_parity_c", "c3", {
|
|
344
414
|
count: 2,
|
|
345
415
|
flag: !1
|
|
346
416
|
});
|
|
347
|
-
|
|
417
|
+
S(t.flag).not.toEqual(e.flag);
|
|
348
418
|
let r = {
|
|
349
419
|
invocationId: "inv2",
|
|
350
420
|
subject: null,
|
|
@@ -356,13 +426,13 @@ var w = (e) => ({
|
|
|
356
426
|
next: t
|
|
357
427
|
}]
|
|
358
428
|
};
|
|
359
|
-
await
|
|
360
|
-
let
|
|
361
|
-
...
|
|
362
|
-
predicate:
|
|
429
|
+
await h(n.store, _(r));
|
|
430
|
+
let i = (await n.store.query({
|
|
431
|
+
...k("voltro_parity_c"),
|
|
432
|
+
predicate: a("id", "c3")
|
|
363
433
|
}))[0];
|
|
364
|
-
|
|
365
|
-
}),
|
|
434
|
+
S(i.flag).toEqual(e.flag), S(i.count).toEqual(e.count);
|
|
435
|
+
}), C("undo: reverts a DELETE (inverse re-insert) on this dialect", async () => {
|
|
366
436
|
let e = await n.store.insert("voltro_parity_c", {
|
|
367
437
|
id: "c4",
|
|
368
438
|
title: "gone",
|
|
@@ -380,24 +450,24 @@ var w = (e) => ({
|
|
|
380
450
|
prev: e
|
|
381
451
|
}]
|
|
382
452
|
};
|
|
383
|
-
await
|
|
453
|
+
await h(n.store, _(t));
|
|
384
454
|
let r = (await n.store.query({
|
|
385
|
-
...
|
|
386
|
-
predicate:
|
|
455
|
+
...k("voltro_parity_c"),
|
|
456
|
+
predicate: a("id", "c4")
|
|
387
457
|
}))[0];
|
|
388
|
-
|
|
389
|
-
}),
|
|
458
|
+
S(r.id).toBe("c4"), S(r.title).toBe("gone"), S(r.flag).toEqual(e.flag);
|
|
459
|
+
}), C("onChange fires insert + update + delete events", async () => {
|
|
390
460
|
let e = [], t = n.store.onChange((t) => e.push(t.op));
|
|
391
461
|
await n.store.insert("voltro_parity_a", {
|
|
392
462
|
id: "a4",
|
|
393
463
|
title: "a",
|
|
394
464
|
count: 1
|
|
395
|
-
}), await n.store.update("voltro_parity_a", "a4", { title: "b" }), await n.store.delete("voltro_parity_a", "a4"), t(),
|
|
465
|
+
}), await n.store.update("voltro_parity_a", "a4", { title: "b" }), await n.store.delete("voltro_parity_a", "a4"), t(), S(e).toEqual([
|
|
396
466
|
"insert",
|
|
397
467
|
"update",
|
|
398
468
|
"delete"
|
|
399
469
|
]);
|
|
400
|
-
}),
|
|
470
|
+
}), C("binding more than 10 change listeners emits no MaxListeners warning", async () => {
|
|
401
471
|
let e = [], t = (t) => {
|
|
402
472
|
e.push(t.name);
|
|
403
473
|
};
|
|
@@ -405,19 +475,19 @@ var w = (e) => ({
|
|
|
405
475
|
let r = Array.from({ length: 32 }, () => n.store.onChange(() => {}));
|
|
406
476
|
await new Promise((e) => setImmediate(e));
|
|
407
477
|
for (let e of r) e();
|
|
408
|
-
process.off("warning", t),
|
|
409
|
-
}),
|
|
478
|
+
process.off("warning", t), S(e.filter((e) => e === "MaxListenersExceededWarning"), "the change bus still warns at 11 listeners — `raiseChangeListenerCeiling` is not wired into this dialect’s store").toEqual([]);
|
|
479
|
+
}), C("transactional rolls back on throw + drops queued events", async () => {
|
|
410
480
|
let e = [], t = n.store.onChange((t) => e.push(t.op));
|
|
411
|
-
await
|
|
481
|
+
await S(n.store.transactional(async (e) => {
|
|
412
482
|
throw await e.insert("voltro_parity_a", {
|
|
413
483
|
id: "rollback",
|
|
414
484
|
title: "r",
|
|
415
485
|
count: 0
|
|
416
486
|
}), Error("intentional rollback");
|
|
417
|
-
})).rejects.toThrow("intentional rollback"), t(),
|
|
418
|
-
let r = await n.store.query(
|
|
419
|
-
|
|
420
|
-
}),
|
|
487
|
+
})).rejects.toThrow("intentional rollback"), t(), S(e).toEqual([]);
|
|
488
|
+
let r = await n.store.query(k("voltro_parity_a"));
|
|
489
|
+
S(r.length).toBe(0);
|
|
490
|
+
}), C("transactional commit drains queued events in order", async () => {
|
|
421
491
|
let e = [], t = n.store.onChange((t) => e.push(t.op));
|
|
422
492
|
await n.store.transactional(async (e) => {
|
|
423
493
|
await e.insert("voltro_parity_a", {
|
|
@@ -429,8 +499,8 @@ var w = (e) => ({
|
|
|
429
499
|
title: "b",
|
|
430
500
|
count: 2
|
|
431
501
|
});
|
|
432
|
-
}), t(),
|
|
433
|
-
}),
|
|
502
|
+
}), t(), S(e).toEqual(["insert", "insert"]);
|
|
503
|
+
}), C("upsert inserts a new row, then updates on conflict — no duplicate", async () => {
|
|
434
504
|
await n.store.upsert("voltro_parity_a", {
|
|
435
505
|
id: "u1",
|
|
436
506
|
title: "first",
|
|
@@ -443,13 +513,370 @@ var w = (e) => ({
|
|
|
443
513
|
conflictColumns: ["id"],
|
|
444
514
|
update: ["title", "count"]
|
|
445
515
|
});
|
|
446
|
-
let e = await n.store.query(
|
|
447
|
-
|
|
516
|
+
let e = await n.store.query(k("voltro_parity_a"));
|
|
517
|
+
S(e.length).toBe(1), S(e[0]).toMatchObject({
|
|
448
518
|
id: "u1",
|
|
449
519
|
title: "second",
|
|
450
520
|
count: 2
|
|
451
521
|
});
|
|
452
|
-
}),
|
|
522
|
+
}), x.each(["direct", "transactional"])("JSON patch and column onUpdate (%s)", (e) => {
|
|
523
|
+
let t = (t) => e === "transactional" ? n.store.transactional(t) : t(n.store), r = [
|
|
524
|
+
{
|
|
525
|
+
name: "shallow object merge retains unrelated keys and nulls",
|
|
526
|
+
before: {
|
|
527
|
+
keep: 1,
|
|
528
|
+
child: { old: !0 }
|
|
529
|
+
},
|
|
530
|
+
path: "metadata",
|
|
531
|
+
value: {
|
|
532
|
+
child: { new: !0 },
|
|
533
|
+
added: null
|
|
534
|
+
},
|
|
535
|
+
after: {
|
|
536
|
+
keep: 1,
|
|
537
|
+
child: { new: !0 },
|
|
538
|
+
added: null
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: "array replacement",
|
|
543
|
+
before: { keep: 1 },
|
|
544
|
+
path: "metadata",
|
|
545
|
+
value: [
|
|
546
|
+
1,
|
|
547
|
+
null,
|
|
548
|
+
!1
|
|
549
|
+
],
|
|
550
|
+
after: [
|
|
551
|
+
1,
|
|
552
|
+
null,
|
|
553
|
+
!1
|
|
554
|
+
]
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: "empty object replaces an array",
|
|
558
|
+
before: ["old"],
|
|
559
|
+
path: "metadata",
|
|
560
|
+
value: {},
|
|
561
|
+
after: {}
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "object from SQL NULL",
|
|
565
|
+
before: null,
|
|
566
|
+
path: "metadata",
|
|
567
|
+
value: { a: !1 },
|
|
568
|
+
after: { a: !1 }
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: "Unicode keys and values survive storage and patching",
|
|
572
|
+
before: {
|
|
573
|
+
雪: { keep: "Ελληνικά" },
|
|
574
|
+
Grüße: "👋"
|
|
575
|
+
},
|
|
576
|
+
path: "metadata.雪.changed",
|
|
577
|
+
value: "こんにちは 🌍",
|
|
578
|
+
after: {
|
|
579
|
+
雪: {
|
|
580
|
+
keep: "Ελληνικά",
|
|
581
|
+
changed: "こんにちは 🌍"
|
|
582
|
+
},
|
|
583
|
+
Grüße: "👋"
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: "missing deep parents",
|
|
588
|
+
before: { keep: 1 },
|
|
589
|
+
path: "metadata.a.b.c",
|
|
590
|
+
value: "deep",
|
|
591
|
+
after: {
|
|
592
|
+
keep: 1,
|
|
593
|
+
a: { b: { c: "deep" } }
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
name: "scalar parent becomes an object",
|
|
598
|
+
before: {
|
|
599
|
+
a: 1,
|
|
600
|
+
keep: !0
|
|
601
|
+
},
|
|
602
|
+
path: "metadata.a.b",
|
|
603
|
+
value: 0,
|
|
604
|
+
after: {
|
|
605
|
+
a: { b: 0 },
|
|
606
|
+
keep: !0
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: "numeric segments are object keys, not array indices",
|
|
611
|
+
before: { a: [{ old: !0 }] },
|
|
612
|
+
path: "metadata.a.0.b",
|
|
613
|
+
value: !1,
|
|
614
|
+
after: { a: { 0: { b: !1 } } }
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
name: "nested siblings survive",
|
|
618
|
+
before: { a: {
|
|
619
|
+
keep: !0,
|
|
620
|
+
b: {
|
|
621
|
+
old: 1,
|
|
622
|
+
c: "before"
|
|
623
|
+
}
|
|
624
|
+
} },
|
|
625
|
+
path: "metadata.a.b.c",
|
|
626
|
+
value: "after",
|
|
627
|
+
after: { a: {
|
|
628
|
+
keep: !0,
|
|
629
|
+
b: {
|
|
630
|
+
old: 1,
|
|
631
|
+
c: "after"
|
|
632
|
+
}
|
|
633
|
+
} }
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: "literal top-level keys with punctuation",
|
|
637
|
+
before: { keep: !0 },
|
|
638
|
+
path: "metadata",
|
|
639
|
+
value: {
|
|
640
|
+
"a.b": null,
|
|
641
|
+
"a\"b": 0,
|
|
642
|
+
"a\\b": !1
|
|
643
|
+
},
|
|
644
|
+
after: {
|
|
645
|
+
keep: !0,
|
|
646
|
+
"a.b": null,
|
|
647
|
+
"a\"b": 0,
|
|
648
|
+
"a\\b": !1
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: "escaped nested keys preserve existing siblings and value types",
|
|
653
|
+
before: {
|
|
654
|
+
"a\"b": {
|
|
655
|
+
keep: [null, !0],
|
|
656
|
+
"c\\d": { old: !0 }
|
|
657
|
+
},
|
|
658
|
+
text: "kept",
|
|
659
|
+
false: !1,
|
|
660
|
+
zero: 0,
|
|
661
|
+
nil: null
|
|
662
|
+
},
|
|
663
|
+
path: "metadata.a\"b.c\\d.changed",
|
|
664
|
+
value: !1,
|
|
665
|
+
after: {
|
|
666
|
+
"a\"b": {
|
|
667
|
+
keep: [null, !0],
|
|
668
|
+
"c\\d": {
|
|
669
|
+
old: !0,
|
|
670
|
+
changed: !1
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
text: "kept",
|
|
674
|
+
false: !1,
|
|
675
|
+
zero: 0,
|
|
676
|
+
nil: null
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: "escaped merge retains unrelated escaped siblings",
|
|
681
|
+
before: {
|
|
682
|
+
"c\\d": [!0, null],
|
|
683
|
+
"literal\\n": !1,
|
|
684
|
+
"line\nkey": 0,
|
|
685
|
+
"a\"b": { old: !0 }
|
|
686
|
+
},
|
|
687
|
+
path: "metadata",
|
|
688
|
+
value: {
|
|
689
|
+
"a\"b": { new: !0 },
|
|
690
|
+
"new\\key": null
|
|
691
|
+
},
|
|
692
|
+
after: {
|
|
693
|
+
"c\\d": [!0, null],
|
|
694
|
+
"literal\\n": !1,
|
|
695
|
+
"line\nkey": 0,
|
|
696
|
+
"a\"b": { new: !0 },
|
|
697
|
+
"new\\key": null
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
...[
|
|
701
|
+
"a\"b",
|
|
702
|
+
"c\\d",
|
|
703
|
+
"x\ny",
|
|
704
|
+
"tab key",
|
|
705
|
+
"literal\\n",
|
|
706
|
+
"slash\\\"quote",
|
|
707
|
+
"multiple\\\\end",
|
|
708
|
+
"literal\\u0061"
|
|
709
|
+
].map((e) => ({
|
|
710
|
+
name: `escaped parent ${JSON.stringify(e)} is decoded exactly once`,
|
|
711
|
+
before: { [e]: { keep: !0 } },
|
|
712
|
+
path: `metadata.${e}.changed`,
|
|
713
|
+
value: !1,
|
|
714
|
+
after: { [e]: {
|
|
715
|
+
keep: !0,
|
|
716
|
+
changed: !1
|
|
717
|
+
} }
|
|
718
|
+
})),
|
|
719
|
+
...[
|
|
720
|
+
null,
|
|
721
|
+
!1,
|
|
722
|
+
0,
|
|
723
|
+
"",
|
|
724
|
+
"O'Brien'); DROP TABLE nowhere; --",
|
|
725
|
+
["a", null],
|
|
726
|
+
{ a: !0 }
|
|
727
|
+
].map((e) => ({
|
|
728
|
+
name: `nested ${JSON.stringify(e)}`,
|
|
729
|
+
before: {
|
|
730
|
+
keep: 1,
|
|
731
|
+
value: "old"
|
|
732
|
+
},
|
|
733
|
+
path: "metadata.value",
|
|
734
|
+
value: e,
|
|
735
|
+
after: {
|
|
736
|
+
keep: 1,
|
|
737
|
+
value: e
|
|
738
|
+
}
|
|
739
|
+
}))
|
|
740
|
+
];
|
|
741
|
+
C.each(r)("$name with companion fields in the same write", async ({ before: e, path: n, value: r, after: i }) => {
|
|
742
|
+
await t(async (t) => {
|
|
743
|
+
await t.insert("voltro_parity_values", {
|
|
744
|
+
id: "patch",
|
|
745
|
+
title: "before",
|
|
746
|
+
metadata: e,
|
|
747
|
+
labels: ["old"]
|
|
748
|
+
});
|
|
749
|
+
let a = await t.patchJson("voltro_parity_values", "patch", n, r, {
|
|
750
|
+
title: "after",
|
|
751
|
+
labels: []
|
|
752
|
+
});
|
|
753
|
+
S(a).toMatchObject({
|
|
754
|
+
id: "patch",
|
|
755
|
+
title: "after"
|
|
756
|
+
}), S(await t.query(k("voltro_parity_values"))).toEqual([{
|
|
757
|
+
id: "patch",
|
|
758
|
+
title: "after",
|
|
759
|
+
metadata: i,
|
|
760
|
+
labels: []
|
|
761
|
+
}]);
|
|
762
|
+
});
|
|
763
|
+
}), C("refuses companion primary-key or JSON-column replacement before writing", async () => {
|
|
764
|
+
await t(async (e) => {
|
|
765
|
+
let t = {
|
|
766
|
+
id: "refuse",
|
|
767
|
+
title: "keep",
|
|
768
|
+
metadata: { keep: !0 },
|
|
769
|
+
labels: []
|
|
770
|
+
};
|
|
771
|
+
await e.insert("voltro_parity_values", t);
|
|
772
|
+
for (let t of [{ id: "renamed" }, { metadata: {} }]) await S(e.patchJson("voltro_parity_values", "refuse", "metadata.value", 1, t)).rejects.toThrow("cannot rewrite");
|
|
773
|
+
S(await e.query(k("voltro_parity_values"))).toEqual([t]), S(await e.patchJson("voltro_parity_values", "missing", "metadata.value", 1, { title: "absent" })).toBeNull();
|
|
774
|
+
});
|
|
775
|
+
}), C("does not confuse JSON columns named key/value with iterator columns", async () => {
|
|
776
|
+
await t(async (e) => {
|
|
777
|
+
await e.insert(I.tableName, {
|
|
778
|
+
id: "names",
|
|
779
|
+
key: { "a\"b": { keep: !0 } },
|
|
780
|
+
value: { "c\\d": { keep: !1 } }
|
|
781
|
+
}), await e.patchJson(I.tableName, "names", "key.a\"b.changed", !1), await e.patchJson(I.tableName, "names", "value.c\\d.changed", !0), S(await e.query(k(I.tableName))).toEqual([{
|
|
782
|
+
id: "names",
|
|
783
|
+
key: { "a\"b": {
|
|
784
|
+
keep: !0,
|
|
785
|
+
changed: !1
|
|
786
|
+
} },
|
|
787
|
+
value: { "c\\d": {
|
|
788
|
+
keep: !1,
|
|
789
|
+
changed: !0
|
|
790
|
+
} }
|
|
791
|
+
}]);
|
|
792
|
+
});
|
|
793
|
+
}), C("executes application onUpdate across keyed, bulk, upsert and JSON writes", async () => {
|
|
794
|
+
let t = g([{
|
|
795
|
+
tableName: N.tableName,
|
|
796
|
+
fields: N.fields
|
|
797
|
+
}, {
|
|
798
|
+
tableName: F.tableName,
|
|
799
|
+
fields: F.fields
|
|
800
|
+
}]), r = v(n.store, {
|
|
801
|
+
subject: {
|
|
802
|
+
type: "anonymous",
|
|
803
|
+
id: null,
|
|
804
|
+
tenantId: null
|
|
805
|
+
},
|
|
806
|
+
schemaRegistry: t
|
|
807
|
+
}), i = async (e) => {
|
|
808
|
+
let t = /* @__PURE__ */ new Date("2026-01-01T00:00:00.000Z"), n = Date.now();
|
|
809
|
+
for (let n of [
|
|
810
|
+
"keyed",
|
|
811
|
+
"bulk",
|
|
812
|
+
"upsert",
|
|
813
|
+
"callback"
|
|
814
|
+
]) await e.insert("voltro_parity_d", {
|
|
815
|
+
id: n,
|
|
816
|
+
title: "before",
|
|
817
|
+
at: t,
|
|
818
|
+
n: 9
|
|
819
|
+
}), n === "keyed" ? await e.update("voltro_parity_d", n, { title: "edited" }) : n === "bulk" ? await e.updateMany("voltro_parity_d", { title: "edited" }, { where: a("id", n) }) : await e.upsert("voltro_parity_d", {
|
|
820
|
+
id: n,
|
|
821
|
+
title: "edited"
|
|
822
|
+
}, {
|
|
823
|
+
conflictColumns: ["id"],
|
|
824
|
+
update: n === "callback" ? () => ({ title: "edited" }) : ["title"]
|
|
825
|
+
});
|
|
826
|
+
let r = await e.query(k("voltro_parity_d"));
|
|
827
|
+
S(r).toHaveLength(4);
|
|
828
|
+
for (let e of r) {
|
|
829
|
+
S(e).toMatchObject({
|
|
830
|
+
title: "edited",
|
|
831
|
+
n: 9
|
|
832
|
+
});
|
|
833
|
+
let t = e.at instanceof Date ? e.at.getTime() : new Date(String(e.at)).getTime();
|
|
834
|
+
S(t).toBeGreaterThanOrEqual(n), S(t).toBeLessThanOrEqual(Date.now());
|
|
835
|
+
}
|
|
836
|
+
await e.insert("voltro_parity_values", {
|
|
837
|
+
id: "automatic",
|
|
838
|
+
title: "original",
|
|
839
|
+
metadata: { keep: !0 },
|
|
840
|
+
labels: []
|
|
841
|
+
}), await e.patchJson("voltro_parity_values", "automatic", "metadata.child.value", !1), S(await e.query(k("voltro_parity_values"))).toEqual([{
|
|
842
|
+
id: "automatic",
|
|
843
|
+
title: "automatic",
|
|
844
|
+
metadata: {
|
|
845
|
+
keep: !0,
|
|
846
|
+
child: { value: !1 }
|
|
847
|
+
},
|
|
848
|
+
labels: []
|
|
849
|
+
}]), S(await e.upsert("voltro_parity_d", {
|
|
850
|
+
id: "new",
|
|
851
|
+
title: "insert"
|
|
852
|
+
}, {
|
|
853
|
+
conflictColumns: ["id"],
|
|
854
|
+
update: []
|
|
855
|
+
})).toMatchObject({ at: null });
|
|
856
|
+
};
|
|
857
|
+
e === "transactional" ? await r.transactional(i) : await i(r);
|
|
858
|
+
});
|
|
859
|
+
}), C("JSON patch companion failure rolls back the JSON write too", async () => {
|
|
860
|
+
let e = {
|
|
861
|
+
id: "atomic-json",
|
|
862
|
+
title: "keep",
|
|
863
|
+
metadata: { keep: !0 },
|
|
864
|
+
labels: []
|
|
865
|
+
};
|
|
866
|
+
await n.store.insert("voltro_parity_values", e), await S(n.store.patchJson("voltro_parity_values", e.id, "metadata.changed", !0, { title: null })).rejects.toThrow(), S(await n.store.query(k("voltro_parity_values"))).toEqual([e]);
|
|
867
|
+
}), C("concurrent JSON patches preserve every sibling and the companion write", async () => {
|
|
868
|
+
await n.store.insert("voltro_parity_values", {
|
|
869
|
+
id: "concurrent-json",
|
|
870
|
+
title: "before",
|
|
871
|
+
metadata: {},
|
|
872
|
+
labels: []
|
|
873
|
+
}), await Promise.all(Array.from({ length: 8 }, (e, t) => n.store.transactional((e) => e.patchJson("voltro_parity_values", "concurrent-json", `metadata.key${t}`, t, { title: "after" })))), S(await n.store.query(k("voltro_parity_values"))).toEqual([{
|
|
874
|
+
id: "concurrent-json",
|
|
875
|
+
title: "after",
|
|
876
|
+
metadata: Object.fromEntries(Array.from({ length: 8 }, (e, t) => [`key${t}`, t])),
|
|
877
|
+
labels: []
|
|
878
|
+
}]);
|
|
879
|
+
}), C("upsert on a NON-primary-key unique constraint updates that row", async () => {
|
|
453
880
|
await n.store.upsert("voltro_parity_upsert", {
|
|
454
881
|
id: "up_first",
|
|
455
882
|
teamId: "t-1",
|
|
@@ -464,14 +891,293 @@ var w = (e) => ({
|
|
|
464
891
|
conflictColumns: ["teamId", "year"],
|
|
465
892
|
update: ["hours"]
|
|
466
893
|
});
|
|
467
|
-
let e = await n.store.query(
|
|
468
|
-
|
|
894
|
+
let e = await n.store.query(k("voltro_parity_upsert"));
|
|
895
|
+
S(e.length, "the second upsert inserted a duplicate instead of updating").toBe(1), S(e[0]).toMatchObject({
|
|
469
896
|
id: "up_first",
|
|
470
897
|
teamId: "t-1",
|
|
471
898
|
year: 2026,
|
|
472
899
|
hours: 111
|
|
473
900
|
});
|
|
474
|
-
}),
|
|
901
|
+
}), x.each(["direct", "transactional"])("upsert updateValues (%s)", (e) => {
|
|
902
|
+
let t = (t) => e === "transactional" ? n.store.transactional(t) : t(n.store);
|
|
903
|
+
C("keeps INSERT defaults and writes omitted columns only on conflict", async () => {
|
|
904
|
+
let e = /* @__PURE__ */ new Date("2026-09-05T10:20:30.000Z"), r = {
|
|
905
|
+
conflictColumns: ["id"],
|
|
906
|
+
update: [],
|
|
907
|
+
updateValues: {
|
|
908
|
+
at: e,
|
|
909
|
+
n: 42
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
await t(async (t) => {
|
|
913
|
+
let n = await t.upsert("voltro_parity_d", {
|
|
914
|
+
id: "constant",
|
|
915
|
+
title: "Lobby"
|
|
916
|
+
}, r);
|
|
917
|
+
S(n).toMatchObject({
|
|
918
|
+
id: "constant",
|
|
919
|
+
title: "Lobby",
|
|
920
|
+
at: null,
|
|
921
|
+
n: null
|
|
922
|
+
});
|
|
923
|
+
let i = await t.upsert("voltro_parity_d", {
|
|
924
|
+
id: "constant",
|
|
925
|
+
title: "Do not overwrite"
|
|
926
|
+
}, r);
|
|
927
|
+
S(i).toMatchObject({
|
|
928
|
+
id: "constant",
|
|
929
|
+
title: "Lobby",
|
|
930
|
+
n: 42
|
|
931
|
+
}), S(new Date(String(i.at)).toISOString()).toBe(e.toISOString());
|
|
932
|
+
});
|
|
933
|
+
let [i] = await n.store.query(k("voltro_parity_d"));
|
|
934
|
+
S(i).toMatchObject({
|
|
935
|
+
id: "constant",
|
|
936
|
+
title: "Lobby",
|
|
937
|
+
n: 42
|
|
938
|
+
}), S(new Date(String(i.at)).toISOString()).toBe(e.toISOString());
|
|
939
|
+
}), C.each([
|
|
940
|
+
"omitted",
|
|
941
|
+
"empty",
|
|
942
|
+
"columns",
|
|
943
|
+
"callback"
|
|
944
|
+
])("overrides the %s projection, retaining row identity", async (e) => {
|
|
945
|
+
await t(async (t) => {
|
|
946
|
+
await t.insert("voltro_parity_upsert", {
|
|
947
|
+
id: "original",
|
|
948
|
+
teamId: "t1",
|
|
949
|
+
year: 2026,
|
|
950
|
+
hours: 1
|
|
951
|
+
});
|
|
952
|
+
let n = e === "callback" ? () => ({ hours: 3 }) : e === "columns" ? ["hours"] : e === "empty" ? [] : void 0, r = await t.upsert("voltro_parity_upsert", {
|
|
953
|
+
id: "unused-new-id",
|
|
954
|
+
teamId: "t1",
|
|
955
|
+
year: 2026,
|
|
956
|
+
hours: 2
|
|
957
|
+
}, {
|
|
958
|
+
conflictColumns: ["teamId", "year"],
|
|
959
|
+
...n === void 0 ? {} : { update: n },
|
|
960
|
+
updateValues: { hours: 42 }
|
|
961
|
+
});
|
|
962
|
+
S(r).toMatchObject({
|
|
963
|
+
id: "original",
|
|
964
|
+
teamId: "t1",
|
|
965
|
+
year: 2026,
|
|
966
|
+
hours: 42
|
|
967
|
+
});
|
|
968
|
+
}), S(await n.store.query(k("voltro_parity_upsert"))).toEqual([S.objectContaining({
|
|
969
|
+
id: "original",
|
|
970
|
+
teamId: "t1",
|
|
971
|
+
year: 2026,
|
|
972
|
+
hours: 42
|
|
973
|
+
})]);
|
|
974
|
+
}), C("binds explicit NULL to timestamp/int columns and omits undefined assignments", async () => {
|
|
975
|
+
await t(async (e) => {
|
|
976
|
+
await e.insert("voltro_parity_d", {
|
|
977
|
+
id: "nulls",
|
|
978
|
+
title: "original",
|
|
979
|
+
at: /* @__PURE__ */ new Date("2026-01-01"),
|
|
980
|
+
n: 9
|
|
981
|
+
});
|
|
982
|
+
let t = {
|
|
983
|
+
id: "nulls",
|
|
984
|
+
title: "input",
|
|
985
|
+
at: /* @__PURE__ */ new Date("2026-02-01"),
|
|
986
|
+
n: 2
|
|
987
|
+
}, n = await e.upsert("voltro_parity_d", t, {
|
|
988
|
+
conflictColumns: ["id"],
|
|
989
|
+
update: ["title"],
|
|
990
|
+
updateValues: {
|
|
991
|
+
title: "",
|
|
992
|
+
at: null,
|
|
993
|
+
n: void 0
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
S(n).toMatchObject({
|
|
997
|
+
id: "nulls",
|
|
998
|
+
title: "",
|
|
999
|
+
at: null,
|
|
1000
|
+
n: 9
|
|
1001
|
+
}), S(await e.upsert("voltro_parity_d", t, {
|
|
1002
|
+
conflictColumns: ["id"],
|
|
1003
|
+
update: [],
|
|
1004
|
+
updateValues: { n: null }
|
|
1005
|
+
})).toMatchObject({
|
|
1006
|
+
id: "nulls",
|
|
1007
|
+
title: "",
|
|
1008
|
+
at: null,
|
|
1009
|
+
n: null
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
}), C("binds false and zero without treating them as omitted", async () => {
|
|
1013
|
+
await t(async (e) => {
|
|
1014
|
+
let t = {
|
|
1015
|
+
id: "falsy",
|
|
1016
|
+
title: "keep",
|
|
1017
|
+
count: 9,
|
|
1018
|
+
flag: !0
|
|
1019
|
+
};
|
|
1020
|
+
await e.insert("voltro_parity_c", t), await e.upsert("voltro_parity_c", t, {
|
|
1021
|
+
conflictColumns: ["id"],
|
|
1022
|
+
update: [],
|
|
1023
|
+
updateValues: {
|
|
1024
|
+
flag: !1,
|
|
1025
|
+
count: 0
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
let n = await e.query(k("voltro_parity_c"));
|
|
1029
|
+
S(n).toEqual([S.objectContaining({
|
|
1030
|
+
id: "falsy",
|
|
1031
|
+
title: "keep",
|
|
1032
|
+
count: 0
|
|
1033
|
+
})]), S([!1, 0]).toContain(n[0].flag);
|
|
1034
|
+
});
|
|
1035
|
+
}), C("encodes JSON/array assignments and binds SQL-looking strings as data", async () => {
|
|
1036
|
+
await t(async (e) => {
|
|
1037
|
+
let t = {
|
|
1038
|
+
id: "structured",
|
|
1039
|
+
title: "original",
|
|
1040
|
+
metadata: { preserve: !0 },
|
|
1041
|
+
labels: ["original"]
|
|
1042
|
+
}, n = {
|
|
1043
|
+
title: "O'Brien'); DROP TABLE nowhere; --",
|
|
1044
|
+
metadata: { nested: [
|
|
1045
|
+
!1,
|
|
1046
|
+
0,
|
|
1047
|
+
null,
|
|
1048
|
+
"a'b"
|
|
1049
|
+
] },
|
|
1050
|
+
labels: [
|
|
1051
|
+
"a",
|
|
1052
|
+
"b'",
|
|
1053
|
+
"c,d"
|
|
1054
|
+
]
|
|
1055
|
+
}, r = {
|
|
1056
|
+
conflictColumns: ["id"],
|
|
1057
|
+
update: [],
|
|
1058
|
+
updateValues: n
|
|
1059
|
+
};
|
|
1060
|
+
await e.upsert("voltro_parity_values", t, r), S(await e.query(k("voltro_parity_values"))).toEqual([t]), await e.upsert("voltro_parity_values", t, r), S(await e.query(k("voltro_parity_values"))).toEqual([{
|
|
1061
|
+
id: "structured",
|
|
1062
|
+
...n
|
|
1063
|
+
}]), await e.upsert("voltro_parity_values", t, {
|
|
1064
|
+
...r,
|
|
1065
|
+
updateValues: {
|
|
1066
|
+
metadata: null,
|
|
1067
|
+
labels: []
|
|
1068
|
+
}
|
|
1069
|
+
}), S(await e.query(k("voltro_parity_values"))).toEqual([{
|
|
1070
|
+
id: "structured",
|
|
1071
|
+
title: n.title,
|
|
1072
|
+
metadata: null,
|
|
1073
|
+
labels: []
|
|
1074
|
+
}]);
|
|
1075
|
+
});
|
|
1076
|
+
}), C("keeps structured raw-driver values working through all write verbs", async () => {
|
|
1077
|
+
await t(async (e) => {
|
|
1078
|
+
let t = {
|
|
1079
|
+
title: "before",
|
|
1080
|
+
metadata: { before: [1] },
|
|
1081
|
+
labels: ["before"]
|
|
1082
|
+
}, n = {
|
|
1083
|
+
title: "after",
|
|
1084
|
+
metadata: { after: [null, !1] },
|
|
1085
|
+
labels: ["after"]
|
|
1086
|
+
};
|
|
1087
|
+
await e.insert("voltro_parity_values", {
|
|
1088
|
+
id: "single",
|
|
1089
|
+
...t
|
|
1090
|
+
}), await e.insertMany("voltro_parity_values", [{
|
|
1091
|
+
id: "bulk",
|
|
1092
|
+
...t
|
|
1093
|
+
}, {
|
|
1094
|
+
id: "callback",
|
|
1095
|
+
...t
|
|
1096
|
+
}]), await e.update("voltro_parity_values", "single", n), await e.updateMany("voltro_parity_values", n, { where: a("id", "bulk") }), await e.upsert("voltro_parity_values", {
|
|
1097
|
+
id: "callback",
|
|
1098
|
+
...t
|
|
1099
|
+
}, {
|
|
1100
|
+
conflictColumns: ["id"],
|
|
1101
|
+
update: () => n
|
|
1102
|
+
}), await e.insertIgnore("voltro_parity_values", {
|
|
1103
|
+
id: "single",
|
|
1104
|
+
...t
|
|
1105
|
+
}, { conflictColumns: ["id"] }), await e.insertIgnore("voltro_parity_values", {
|
|
1106
|
+
id: "ignore-new",
|
|
1107
|
+
...n
|
|
1108
|
+
}, { conflictColumns: ["id"] });
|
|
1109
|
+
let r = await e.query(k("voltro_parity_values"));
|
|
1110
|
+
S([...r].sort((e, t) => String(e.id).localeCompare(String(t.id)))).toEqual([
|
|
1111
|
+
{
|
|
1112
|
+
id: "bulk",
|
|
1113
|
+
...n
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
id: "callback",
|
|
1117
|
+
...n
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
id: "ignore-new",
|
|
1121
|
+
...n
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
id: "single",
|
|
1125
|
+
...n
|
|
1126
|
+
}
|
|
1127
|
+
]);
|
|
1128
|
+
});
|
|
1129
|
+
}), C.each([
|
|
1130
|
+
"id",
|
|
1131
|
+
"teamId",
|
|
1132
|
+
"year"
|
|
1133
|
+
])("refuses updateValues.%s before either branch writes", async (e) => {
|
|
1134
|
+
await t(async (t) => {
|
|
1135
|
+
let n = {
|
|
1136
|
+
id: "identity",
|
|
1137
|
+
teamId: "t1",
|
|
1138
|
+
year: 2026,
|
|
1139
|
+
hours: 1
|
|
1140
|
+
}, r = {
|
|
1141
|
+
conflictColumns: ["teamId", "year"],
|
|
1142
|
+
update: [],
|
|
1143
|
+
updateValues: { [e]: "different" }
|
|
1144
|
+
};
|
|
1145
|
+
await S(t.upsert("voltro_parity_upsert", n, r)).rejects.toThrow("identity"), S(await t.query(k("voltro_parity_upsert"))).toEqual([]), await t.insert("voltro_parity_upsert", n), await S(t.upsert("voltro_parity_upsert", n, r)).rejects.toThrow("identity"), S(await t.query(k("voltro_parity_upsert"))).toEqual([S.objectContaining(n)]);
|
|
1146
|
+
});
|
|
1147
|
+
});
|
|
1148
|
+
}), C("upsert updateValues rolls back row changes and discards transaction events", async () => {
|
|
1149
|
+
await n.store.insert("voltro_parity_a", {
|
|
1150
|
+
id: "rollback-values",
|
|
1151
|
+
title: "keep",
|
|
1152
|
+
count: 1
|
|
1153
|
+
});
|
|
1154
|
+
let e = [], t = n.store.onChange((t) => {
|
|
1155
|
+
e.push(t);
|
|
1156
|
+
});
|
|
1157
|
+
try {
|
|
1158
|
+
await S(n.store.transactional(async (e) => {
|
|
1159
|
+
let t = await e.upsert("voltro_parity_a", {
|
|
1160
|
+
id: "rollback-values",
|
|
1161
|
+
title: "incoming",
|
|
1162
|
+
count: 2
|
|
1163
|
+
}, {
|
|
1164
|
+
conflictColumns: ["id"],
|
|
1165
|
+
update: [],
|
|
1166
|
+
updateValues: { count: 42 }
|
|
1167
|
+
});
|
|
1168
|
+
throw S(t).toMatchObject({
|
|
1169
|
+
title: "keep",
|
|
1170
|
+
count: 42
|
|
1171
|
+
}), Error("rollback values");
|
|
1172
|
+
})).rejects.toThrow("rollback values"), S(await n.store.query(k("voltro_parity_a"))).toEqual([S.objectContaining({
|
|
1173
|
+
id: "rollback-values",
|
|
1174
|
+
title: "keep",
|
|
1175
|
+
count: 1
|
|
1176
|
+
})]), S(e).toEqual([]);
|
|
1177
|
+
} finally {
|
|
1178
|
+
t();
|
|
1179
|
+
}
|
|
1180
|
+
}), C("and a DIFFERENT row is still not silently overwritten", async () => {
|
|
475
1181
|
await n.store.upsert("voltro_parity_upsert", {
|
|
476
1182
|
id: "up_a",
|
|
477
1183
|
teamId: "t-1",
|
|
@@ -486,16 +1192,16 @@ var w = (e) => ({
|
|
|
486
1192
|
conflictColumns: ["teamId", "year"],
|
|
487
1193
|
update: ["hours"]
|
|
488
1194
|
});
|
|
489
|
-
let e = await n.store.query(
|
|
490
|
-
|
|
491
|
-
}),
|
|
1195
|
+
let e = await n.store.query(k("voltro_parity_upsert"));
|
|
1196
|
+
S(e.length).toBe(2), S([...e].map((e) => e.hours).sort()).toEqual([1, 2]);
|
|
1197
|
+
}), C("insert + update bind an explicit NULL into a datetime2/int column", async () => {
|
|
492
1198
|
let e = await n.store.insert("voltro_parity_d", {
|
|
493
1199
|
id: "nd1",
|
|
494
1200
|
title: "nulls",
|
|
495
1201
|
at: null,
|
|
496
1202
|
n: null
|
|
497
1203
|
});
|
|
498
|
-
|
|
1204
|
+
S(e).toMatchObject({
|
|
499
1205
|
id: "nd1",
|
|
500
1206
|
title: "nulls",
|
|
501
1207
|
at: null,
|
|
@@ -510,12 +1216,12 @@ var w = (e) => ({
|
|
|
510
1216
|
at: null,
|
|
511
1217
|
n: null
|
|
512
1218
|
});
|
|
513
|
-
|
|
1219
|
+
S(t).toMatchObject({
|
|
514
1220
|
id: "nd2",
|
|
515
1221
|
at: null,
|
|
516
1222
|
n: null
|
|
517
1223
|
});
|
|
518
|
-
}),
|
|
1224
|
+
}), C("updateMany updates only the rows matching the predicate + returns the count", async () => {
|
|
519
1225
|
await n.store.insert("voltro_parity_a", {
|
|
520
1226
|
id: "m1",
|
|
521
1227
|
title: "x",
|
|
@@ -534,17 +1240,17 @@ var w = (e) => ({
|
|
|
534
1240
|
op: "eq",
|
|
535
1241
|
value: 5
|
|
536
1242
|
} });
|
|
537
|
-
|
|
1243
|
+
S(e).toBe(2);
|
|
538
1244
|
let t = await n.store.query({
|
|
539
|
-
...
|
|
1245
|
+
...k("voltro_parity_a"),
|
|
540
1246
|
predicate: {
|
|
541
1247
|
column: "title",
|
|
542
1248
|
op: "eq",
|
|
543
1249
|
value: "hit"
|
|
544
1250
|
}
|
|
545
1251
|
});
|
|
546
|
-
|
|
547
|
-
}),
|
|
1252
|
+
S(t.length).toBe(2);
|
|
1253
|
+
}), C("updateMany with an AND predicate is an atomic compare-and-set (the wakeup-claim pattern)", async () => {
|
|
548
1254
|
await n.store.insert("voltro_parity_a", {
|
|
549
1255
|
id: "cas",
|
|
550
1256
|
title: "pending",
|
|
@@ -559,10 +1265,10 @@ var w = (e) => ({
|
|
|
559
1265
|
op: "eq",
|
|
560
1266
|
value: e
|
|
561
1267
|
}] }), t = await n.store.updateMany("voltro_parity_a", { title: "claimed" }, { where: e("pending") });
|
|
562
|
-
|
|
1268
|
+
S(t).toBe(1);
|
|
563
1269
|
let r = await n.store.updateMany("voltro_parity_a", { title: "claimed-again" }, { where: e("pending") });
|
|
564
|
-
|
|
565
|
-
}),
|
|
1270
|
+
S(r).toBe(0);
|
|
1271
|
+
}), C("deleteMany removes only the rows matching the predicate + returns the count", async () => {
|
|
566
1272
|
await n.store.insert("voltro_parity_a", {
|
|
567
1273
|
id: "d1",
|
|
568
1274
|
title: "x",
|
|
@@ -581,10 +1287,10 @@ var w = (e) => ({
|
|
|
581
1287
|
op: "eq",
|
|
582
1288
|
value: 5
|
|
583
1289
|
} });
|
|
584
|
-
|
|
585
|
-
let t = await n.store.query(
|
|
586
|
-
|
|
587
|
-
}),
|
|
1290
|
+
S(e).toBe(2);
|
|
1291
|
+
let t = await n.store.query(k("voltro_parity_a"));
|
|
1292
|
+
S(t.map((e) => e.id)).toEqual(["d3"]);
|
|
1293
|
+
}), C("deleteMany emits one delete ChangeEvent per removed row (old-image)", async () => {
|
|
588
1294
|
await n.store.insert("voltro_parity_a", {
|
|
589
1295
|
id: "e1",
|
|
590
1296
|
title: "gone",
|
|
@@ -606,10 +1312,10 @@ var w = (e) => ({
|
|
|
606
1312
|
op: "eq",
|
|
607
1313
|
value: "gone"
|
|
608
1314
|
} });
|
|
609
|
-
t(),
|
|
1315
|
+
t(), S(r).toBe(2);
|
|
610
1316
|
let i = e.filter((e) => e.op === "delete");
|
|
611
|
-
|
|
612
|
-
}),
|
|
1317
|
+
S(i).toHaveLength(2), S(i.map((e) => e.id).sort()).toEqual(["e1", "e2"]);
|
|
1318
|
+
}), C("deleteMany with no match returns 0 and removes nothing", async () => {
|
|
613
1319
|
await n.store.insert("voltro_parity_a", {
|
|
614
1320
|
id: "k1",
|
|
615
1321
|
title: "keep",
|
|
@@ -620,14 +1326,14 @@ var w = (e) => ({
|
|
|
620
1326
|
op: "eq",
|
|
621
1327
|
value: "absent"
|
|
622
1328
|
} });
|
|
623
|
-
|
|
624
|
-
let t = await n.store.query(
|
|
625
|
-
|
|
626
|
-
}),
|
|
1329
|
+
S(e).toBe(0);
|
|
1330
|
+
let t = await n.store.query(k("voltro_parity_a"));
|
|
1331
|
+
S(t.length).toBe(1);
|
|
1332
|
+
}), C("retryFilter recognises the dialect's own transient codes", () => {
|
|
627
1333
|
let e = t.dialect.retryFilter(/* @__PURE__ */ Error("garbage"));
|
|
628
|
-
|
|
1334
|
+
S(["retry", "noRetry"]).toContain(e);
|
|
629
1335
|
});
|
|
630
1336
|
});
|
|
631
1337
|
};
|
|
632
1338
|
//#endregion
|
|
633
|
-
export {
|
|
1339
|
+
export { E as applySchema, B as runDialectParity };
|