@effect-app/infra 4.0.0-beta.81 → 4.0.0-beta.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +55 -32
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +174 -0
- package/dist/Store/SQL/query.d.ts +34 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +326 -0
- package/dist/Store/SQL.d.ts +4 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +203 -0
- package/dist/Store/index.d.ts +1 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +11 -1
- package/dist/Store/service.d.ts +2 -2
- package/dist/api/routing/middleware/middleware.d.ts +35 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +39 -1
- package/dist/api/setupRequest.d.ts +6 -3
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +11 -6
- package/package.json +19 -5
- package/src/Store/Cosmos.ts +200 -165
- package/src/Store/SQL/Pg.ts +294 -0
- package/src/Store/SQL/query.ts +372 -0
- package/src/Store/SQL.ts +327 -0
- package/src/Store/index.ts +10 -0
- package/src/Store/service.ts +2 -2
- package/src/api/routing/middleware/middleware.ts +43 -0
- package/src/api/setupRequest.ts +24 -4
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/sql-store.test.ts +444 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type Sqlite from "better-sqlite3"
|
|
3
|
+
import BetterSqlite from "better-sqlite3"
|
|
4
|
+
import { describe, expect, it } from "vitest"
|
|
5
|
+
import { buildWhereSQLQuery, pgDialect, sqliteDialect } from "../src/Store/SQL/query.js"
|
|
6
|
+
|
|
7
|
+
const query = (db: Sqlite.Database, sql: string, params: unknown[] = []) =>
|
|
8
|
+
db.prepare(sql).all(...params as any[]) as any[]
|
|
9
|
+
|
|
10
|
+
// --- Query builder unit tests ---
|
|
11
|
+
|
|
12
|
+
describe("SQL query builder (SQLite dialect)", () => {
|
|
13
|
+
it("where eq string", () => {
|
|
14
|
+
const result = buildWhereSQLQuery(
|
|
15
|
+
sqliteDialect,
|
|
16
|
+
"id",
|
|
17
|
+
[{ t: "where", path: "name", op: "eq", value: "John" }],
|
|
18
|
+
"users",
|
|
19
|
+
{}
|
|
20
|
+
)
|
|
21
|
+
expect(result.sql).toContain("json_extract(data, '$.name') = ?")
|
|
22
|
+
expect(result.params).toContain("John")
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it("where eq number", () => {
|
|
26
|
+
const result = buildWhereSQLQuery(
|
|
27
|
+
sqliteDialect,
|
|
28
|
+
"id",
|
|
29
|
+
[{ t: "where", path: "age", op: "eq", value: 25 as any }],
|
|
30
|
+
"users",
|
|
31
|
+
{}
|
|
32
|
+
)
|
|
33
|
+
expect(result.sql).toContain("json_extract(data, '$.age') = ?")
|
|
34
|
+
expect(result.params).toContain(25)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("where gt", () => {
|
|
38
|
+
const result = buildWhereSQLQuery(
|
|
39
|
+
sqliteDialect,
|
|
40
|
+
"id",
|
|
41
|
+
[{ t: "where", path: "age", op: "gt", value: 18 as any }],
|
|
42
|
+
"users",
|
|
43
|
+
{}
|
|
44
|
+
)
|
|
45
|
+
expect(result.sql).toContain("json_extract(data, '$.age') > ?")
|
|
46
|
+
expect(result.params).toContain(18)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("where or", () => {
|
|
50
|
+
const result = buildWhereSQLQuery(
|
|
51
|
+
sqliteDialect,
|
|
52
|
+
"id",
|
|
53
|
+
[
|
|
54
|
+
{ t: "where", path: "name", op: "eq", value: "Alice" },
|
|
55
|
+
{ t: "or", path: "name", op: "eq", value: "Bob" }
|
|
56
|
+
],
|
|
57
|
+
"users",
|
|
58
|
+
{}
|
|
59
|
+
)
|
|
60
|
+
expect(result.sql).toContain("= ?")
|
|
61
|
+
expect(result.sql).toContain("OR")
|
|
62
|
+
expect(result.params).toEqual(expect.arrayContaining(["Alice", "Bob"]))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it("where and", () => {
|
|
66
|
+
const result = buildWhereSQLQuery(
|
|
67
|
+
sqliteDialect,
|
|
68
|
+
"id",
|
|
69
|
+
[
|
|
70
|
+
{ t: "where", path: "name", op: "eq", value: "Alice" },
|
|
71
|
+
{ t: "and", path: "age", op: "gt", value: 18 as any }
|
|
72
|
+
],
|
|
73
|
+
"users",
|
|
74
|
+
{}
|
|
75
|
+
)
|
|
76
|
+
expect(result.sql).toContain("AND")
|
|
77
|
+
expect(result.params).toEqual(expect.arrayContaining(["Alice", 18]))
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it("where in", () => {
|
|
81
|
+
const result = buildWhereSQLQuery(
|
|
82
|
+
sqliteDialect,
|
|
83
|
+
"id",
|
|
84
|
+
[{ t: "where", path: "id", op: "in", value: ["a", "b", "c"] as any }],
|
|
85
|
+
"users",
|
|
86
|
+
{}
|
|
87
|
+
)
|
|
88
|
+
expect(result.sql).toContain("id IN (?, ?, ?)")
|
|
89
|
+
expect(result.params).toEqual(expect.arrayContaining(["a", "b", "c"]))
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it("where null", () => {
|
|
93
|
+
const result = buildWhereSQLQuery(
|
|
94
|
+
sqliteDialect,
|
|
95
|
+
"id",
|
|
96
|
+
[{ t: "where", path: "status", op: "eq", value: null as any }],
|
|
97
|
+
"users",
|
|
98
|
+
{}
|
|
99
|
+
)
|
|
100
|
+
expect(result.sql).toContain("IS NULL")
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("where neq null", () => {
|
|
104
|
+
const result = buildWhereSQLQuery(
|
|
105
|
+
sqliteDialect,
|
|
106
|
+
"id",
|
|
107
|
+
[{ t: "where", path: "status", op: "neq", value: null as any }],
|
|
108
|
+
"users",
|
|
109
|
+
{}
|
|
110
|
+
)
|
|
111
|
+
expect(result.sql).toContain("IS NOT NULL")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("where contains", () => {
|
|
115
|
+
const result = buildWhereSQLQuery(
|
|
116
|
+
sqliteDialect,
|
|
117
|
+
"id",
|
|
118
|
+
[{ t: "where", path: "name", op: "contains", value: "oh" }],
|
|
119
|
+
"users",
|
|
120
|
+
{}
|
|
121
|
+
)
|
|
122
|
+
expect(result.sql).toContain("LIKE")
|
|
123
|
+
expect(result.sql).toContain("LOWER")
|
|
124
|
+
expect(result.params).toContain("%oh%")
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it("where startsWith", () => {
|
|
128
|
+
const result = buildWhereSQLQuery(
|
|
129
|
+
sqliteDialect,
|
|
130
|
+
"id",
|
|
131
|
+
[{ t: "where", path: "name", op: "startsWith", value: "Jo" }],
|
|
132
|
+
"users",
|
|
133
|
+
{}
|
|
134
|
+
)
|
|
135
|
+
expect(result.sql).toContain("LIKE")
|
|
136
|
+
expect(result.params).toContain("Jo%")
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("where endsWith", () => {
|
|
140
|
+
const result = buildWhereSQLQuery(
|
|
141
|
+
sqliteDialect,
|
|
142
|
+
"id",
|
|
143
|
+
[{ t: "where", path: "name", op: "endsWith", value: "hn" }],
|
|
144
|
+
"users",
|
|
145
|
+
{}
|
|
146
|
+
)
|
|
147
|
+
expect(result.sql).toContain("LIKE")
|
|
148
|
+
expect(result.params).toContain("%hn")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it("where includes (array contains)", () => {
|
|
152
|
+
const result = buildWhereSQLQuery(
|
|
153
|
+
sqliteDialect,
|
|
154
|
+
"id",
|
|
155
|
+
[{ t: "where", path: "tags", op: "includes", value: "admin" }],
|
|
156
|
+
"users",
|
|
157
|
+
{}
|
|
158
|
+
)
|
|
159
|
+
expect(result.sql).toContain("json_each")
|
|
160
|
+
expect(result.sql).toContain("value = ?")
|
|
161
|
+
expect(result.params).toContain("admin")
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("where includes-any (array contains any)", () => {
|
|
165
|
+
const result = buildWhereSQLQuery(
|
|
166
|
+
sqliteDialect,
|
|
167
|
+
"id",
|
|
168
|
+
[{ t: "where", path: "tags", op: "includes-any", value: ["admin", "user"] as any }],
|
|
169
|
+
"users",
|
|
170
|
+
{}
|
|
171
|
+
)
|
|
172
|
+
expect(result.sql).toContain("json_each")
|
|
173
|
+
expect(result.sql).toContain("IN")
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it("nested scopes", () => {
|
|
177
|
+
const result = buildWhereSQLQuery(
|
|
178
|
+
sqliteDialect,
|
|
179
|
+
"id",
|
|
180
|
+
[
|
|
181
|
+
{ t: "where", path: "a", op: "eq", value: "1" },
|
|
182
|
+
{
|
|
183
|
+
t: "or-scope",
|
|
184
|
+
result: [
|
|
185
|
+
{ t: "where", path: "b", op: "eq", value: "2" },
|
|
186
|
+
{ t: "and", path: "c", op: "eq", value: "3" }
|
|
187
|
+
],
|
|
188
|
+
relation: "some" as const
|
|
189
|
+
}
|
|
190
|
+
],
|
|
191
|
+
"test",
|
|
192
|
+
{}
|
|
193
|
+
)
|
|
194
|
+
expect(result.sql).toContain("OR (")
|
|
195
|
+
expect(result.sql).toContain("AND")
|
|
196
|
+
expect(result.params).toEqual(expect.arrayContaining(["1", "2", "3"]))
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it("id key maps to id column", () => {
|
|
200
|
+
const result = buildWhereSQLQuery(
|
|
201
|
+
sqliteDialect,
|
|
202
|
+
"myId",
|
|
203
|
+
[{ t: "where", path: "myId", op: "eq", value: "123" }],
|
|
204
|
+
"users",
|
|
205
|
+
{}
|
|
206
|
+
)
|
|
207
|
+
expect(result.sql).toContain("id = ?")
|
|
208
|
+
expect(result.sql).not.toContain("json_extract")
|
|
209
|
+
expect(result.params).toContain("123")
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it("order + limit + skip", () => {
|
|
213
|
+
const result = buildWhereSQLQuery(
|
|
214
|
+
sqliteDialect,
|
|
215
|
+
"id",
|
|
216
|
+
[],
|
|
217
|
+
"users",
|
|
218
|
+
{},
|
|
219
|
+
undefined,
|
|
220
|
+
[{ key: "name", direction: "ASC" }] as any,
|
|
221
|
+
5,
|
|
222
|
+
10
|
|
223
|
+
)
|
|
224
|
+
expect(result.sql).toContain("ORDER BY")
|
|
225
|
+
expect(result.sql).toContain("ASC")
|
|
226
|
+
expect(result.sql).toContain("LIMIT")
|
|
227
|
+
expect(result.sql).toContain("OFFSET")
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe("SQL query builder (PostgreSQL dialect)", () => {
|
|
232
|
+
it("where eq string uses ->> operator", () => {
|
|
233
|
+
const result = buildWhereSQLQuery(
|
|
234
|
+
pgDialect,
|
|
235
|
+
"id",
|
|
236
|
+
[{ t: "where", path: "name", op: "eq", value: "John" }],
|
|
237
|
+
"users",
|
|
238
|
+
{}
|
|
239
|
+
)
|
|
240
|
+
expect(result.sql).toContain("data->>'name'")
|
|
241
|
+
expect(result.sql).toContain("$1")
|
|
242
|
+
expect(result.params).toContain("John")
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it("where contains uses ILIKE", () => {
|
|
246
|
+
const result = buildWhereSQLQuery(
|
|
247
|
+
pgDialect,
|
|
248
|
+
"id",
|
|
249
|
+
[{ t: "where", path: "name", op: "contains", value: "oh" }],
|
|
250
|
+
"users",
|
|
251
|
+
{}
|
|
252
|
+
)
|
|
253
|
+
expect(result.sql).toContain("ILIKE")
|
|
254
|
+
expect(result.params).toContain("%oh%")
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it("where in uses $N placeholders", () => {
|
|
258
|
+
const result = buildWhereSQLQuery(
|
|
259
|
+
pgDialect,
|
|
260
|
+
"id",
|
|
261
|
+
[{ t: "where", path: "status", op: "in", value: ["active", "pending"] as any }],
|
|
262
|
+
"users",
|
|
263
|
+
{}
|
|
264
|
+
)
|
|
265
|
+
expect(result.sql).toContain("$1")
|
|
266
|
+
expect(result.sql).toContain("$2")
|
|
267
|
+
expect(result.params).toEqual(expect.arrayContaining(["active", "pending"]))
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it("where includes uses @> jsonb operator", () => {
|
|
271
|
+
const result = buildWhereSQLQuery(
|
|
272
|
+
pgDialect,
|
|
273
|
+
"id",
|
|
274
|
+
[{ t: "where", path: "tags", op: "includes", value: "admin" }],
|
|
275
|
+
"users",
|
|
276
|
+
{}
|
|
277
|
+
)
|
|
278
|
+
expect(result.sql).toContain("@>")
|
|
279
|
+
expect(result.sql).toContain("jsonb")
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it("nested path uses chained -> operators", () => {
|
|
283
|
+
const result = buildWhereSQLQuery(
|
|
284
|
+
pgDialect,
|
|
285
|
+
"id",
|
|
286
|
+
[{ t: "where", path: "address.city", op: "eq", value: "NYC" }],
|
|
287
|
+
"users",
|
|
288
|
+
{}
|
|
289
|
+
)
|
|
290
|
+
expect(result.sql).toContain("data->'address'->>'city'")
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// --- Integration tests with in-memory SQLite (direct, no Effect SQL client) ---
|
|
295
|
+
|
|
296
|
+
describe("SQL Store (SQLite integration)", () => {
|
|
297
|
+
const withDb = (fn: (db: Sqlite.Database) => void) => {
|
|
298
|
+
const db = new BetterSqlite(":memory:")
|
|
299
|
+
db.pragma("journal_mode = WAL")
|
|
300
|
+
try {
|
|
301
|
+
fn(db)
|
|
302
|
+
} finally {
|
|
303
|
+
db.close()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
it("creates table and seeds data", () =>
|
|
308
|
+
withDb((db) => {
|
|
309
|
+
db.exec(
|
|
310
|
+
`CREATE TABLE IF NOT EXISTS "test_items" (id TEXT PRIMARY KEY, _etag TEXT, data JSON NOT NULL)`
|
|
311
|
+
)
|
|
312
|
+
db.prepare(`INSERT INTO "test_items" (id, _etag, data) VALUES (?, ?, ?)`)
|
|
313
|
+
.run("1", "etag1", JSON.stringify({ id: "1", name: "Alice", age: 30 }))
|
|
314
|
+
|
|
315
|
+
const rows = db.prepare(`SELECT * FROM "test_items"`).all()
|
|
316
|
+
expect(rows.length).toBe(1)
|
|
317
|
+
expect((rows[0] as any).id).toBe("1")
|
|
318
|
+
}))
|
|
319
|
+
|
|
320
|
+
it("query builder generates valid SQL for SQLite", () =>
|
|
321
|
+
withDb((db) => {
|
|
322
|
+
db.exec(
|
|
323
|
+
`CREATE TABLE IF NOT EXISTS "test_people" (id TEXT PRIMARY KEY, _etag TEXT, data JSON NOT NULL)`
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
const people = [
|
|
327
|
+
{ id: "1", name: "Alice", age: 30, tags: ["admin", "user"] },
|
|
328
|
+
{ id: "2", name: "Bob", age: 25, tags: ["user"] },
|
|
329
|
+
{ id: "3", name: "Charlie", age: 35, tags: ["admin"] },
|
|
330
|
+
{ id: "4", name: "Diana", age: 28, tags: ["user", "editor"] }
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
const insert = db.prepare(
|
|
334
|
+
`INSERT INTO "test_people" (id, _etag, data) VALUES (?, ?, ?)`
|
|
335
|
+
)
|
|
336
|
+
for (const p of people) {
|
|
337
|
+
insert.run(p.id, `etag_${p.id}`, JSON.stringify(p))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Test eq
|
|
341
|
+
const q1 = buildWhereSQLQuery(
|
|
342
|
+
sqliteDialect, "id",
|
|
343
|
+
[{ t: "where", path: "name", op: "eq", value: "Alice" }],
|
|
344
|
+
"test_people", {}
|
|
345
|
+
)
|
|
346
|
+
expect(query(db, q1.sql, q1.params).length).toBe(1)
|
|
347
|
+
expect((JSON.parse((query(db, q1.sql, q1.params)[0] as any).data) as any).name).toBe("Alice")
|
|
348
|
+
|
|
349
|
+
// Test gt
|
|
350
|
+
const q2 = buildWhereSQLQuery(
|
|
351
|
+
sqliteDialect, "id",
|
|
352
|
+
[{ t: "where", path: "age", op: "gt", value: 28 as any }],
|
|
353
|
+
"test_people", {}
|
|
354
|
+
)
|
|
355
|
+
expect(query(db, q2.sql, q2.params).length).toBe(2)
|
|
356
|
+
|
|
357
|
+
// Test OR
|
|
358
|
+
const q3 = buildWhereSQLQuery(
|
|
359
|
+
sqliteDialect, "id",
|
|
360
|
+
[
|
|
361
|
+
{ t: "where", path: "name", op: "eq", value: "Alice" },
|
|
362
|
+
{ t: "or", path: "name", op: "eq", value: "Bob" }
|
|
363
|
+
],
|
|
364
|
+
"test_people", {}
|
|
365
|
+
)
|
|
366
|
+
expect(query(db, q3.sql, q3.params).length).toBe(2)
|
|
367
|
+
|
|
368
|
+
// Test AND
|
|
369
|
+
const q4 = buildWhereSQLQuery(
|
|
370
|
+
sqliteDialect, "id",
|
|
371
|
+
[
|
|
372
|
+
{ t: "where", path: "name", op: "eq", value: "Alice" },
|
|
373
|
+
{ t: "and", path: "age", op: "gt", value: 25 as any }
|
|
374
|
+
],
|
|
375
|
+
"test_people", {}
|
|
376
|
+
)
|
|
377
|
+
const r4 = query(db, q4.sql, q4.params)
|
|
378
|
+
expect(r4.length).toBe(1)
|
|
379
|
+
expect((JSON.parse((r4[0] as any).data) as any).name).toBe("Alice")
|
|
380
|
+
|
|
381
|
+
// Test IN
|
|
382
|
+
const q5 = buildWhereSQLQuery(
|
|
383
|
+
sqliteDialect, "id",
|
|
384
|
+
[{ t: "where", path: "id", op: "in", value: ["1", "3"] as any }],
|
|
385
|
+
"test_people", {}
|
|
386
|
+
)
|
|
387
|
+
expect(query(db, q5.sql, q5.params).length).toBe(2)
|
|
388
|
+
|
|
389
|
+
// Test contains (string)
|
|
390
|
+
const q6 = buildWhereSQLQuery(
|
|
391
|
+
sqliteDialect, "id",
|
|
392
|
+
[{ t: "where", path: "name", op: "contains", value: "li" }],
|
|
393
|
+
"test_people", {}
|
|
394
|
+
)
|
|
395
|
+
expect(query(db, q6.sql, q6.params).length).toBe(2) // Alice, Charlie
|
|
396
|
+
|
|
397
|
+
// Test startsWith
|
|
398
|
+
const q7 = buildWhereSQLQuery(
|
|
399
|
+
sqliteDialect, "id",
|
|
400
|
+
[{ t: "where", path: "name", op: "startsWith", value: "Al" }],
|
|
401
|
+
"test_people", {}
|
|
402
|
+
)
|
|
403
|
+
const r7 = query(db, q7.sql, q7.params)
|
|
404
|
+
expect(r7.length).toBe(1)
|
|
405
|
+
expect((JSON.parse((r7[0] as any).data) as any).name).toBe("Alice")
|
|
406
|
+
|
|
407
|
+
// Test includes (array)
|
|
408
|
+
const q8 = buildWhereSQLQuery(
|
|
409
|
+
sqliteDialect, "id",
|
|
410
|
+
[{ t: "where", path: "tags", op: "includes", value: "admin" }],
|
|
411
|
+
"test_people", {}
|
|
412
|
+
)
|
|
413
|
+
expect(query(db, q8.sql, q8.params).length).toBe(2) // Alice, Charlie
|
|
414
|
+
|
|
415
|
+
// Test nested scope: where name = Alice OR (age > 30 AND name contains 'ar')
|
|
416
|
+
const q9 = buildWhereSQLQuery(
|
|
417
|
+
sqliteDialect, "id",
|
|
418
|
+
[
|
|
419
|
+
{ t: "where", path: "name", op: "eq", value: "Alice" },
|
|
420
|
+
{
|
|
421
|
+
t: "or-scope",
|
|
422
|
+
result: [
|
|
423
|
+
{ t: "where", path: "age", op: "gt", value: 30 as any },
|
|
424
|
+
{ t: "and", path: "name", op: "contains", value: "ar" }
|
|
425
|
+
],
|
|
426
|
+
relation: "some"
|
|
427
|
+
}
|
|
428
|
+
],
|
|
429
|
+
"test_people", {}
|
|
430
|
+
)
|
|
431
|
+
expect(query(db, q9.sql, q9.params).length).toBe(2) // Alice + Charlie
|
|
432
|
+
|
|
433
|
+
// Test order + limit
|
|
434
|
+
const q10 = buildWhereSQLQuery(
|
|
435
|
+
sqliteDialect, "id", [], "test_people", {},
|
|
436
|
+
undefined,
|
|
437
|
+
[{ key: "age", direction: "DESC" }] as any,
|
|
438
|
+
undefined, 2
|
|
439
|
+
)
|
|
440
|
+
const r10 = query(db, q10.sql, q10.params)
|
|
441
|
+
expect(r10.length).toBe(2)
|
|
442
|
+
expect((JSON.parse((r10[0] as any).data) as any).name).toBe("Charlie") // oldest first
|
|
443
|
+
}))
|
|
444
|
+
})
|