@edge-base/server 0.2.2 → 0.2.4
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/admin-build/_app/immutable/chunks/{C85dMlzL.js → 5RQRbp5q.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B8DT4fss.js → BME_U9TJ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BaCHY17I.js → BYI6CUvd.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BWyDPAjM.js → BgDzp0i0.js} +1 -1
- package/admin-build/_app/immutable/chunks/{c5iKSdWY.js → BjWZuf8W.js} +1 -1
- package/admin-build/_app/immutable/chunks/{g3ZZdY-r.js → C6lpZLE2.js} +1 -1
- package/admin-build/_app/immutable/chunks/{C-DsDCNG.js → D5GswVnI.js} +3 -3
- package/admin-build/_app/immutable/chunks/DBsVqhuh.js +1 -0
- package/admin-build/_app/immutable/chunks/{BEYYl662.js → DYaCRWMA.js} +1 -1
- package/admin-build/_app/immutable/chunks/D__dwMuW.js +1 -0
- package/admin-build/_app/immutable/chunks/{4vlsb8ej.js → Dj-E9-FO.js} +1 -1
- package/admin-build/_app/immutable/chunks/{kiJ6KthZ.js → Dj0QUuOf.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BKXmgPq4.js → XQM1k9PM.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CTngeX8H.js → fYEKMQ-Z.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CPdXvRUb.js → g_-Kpxu3.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DzXaj-Ja.js → wCNueVYy.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.BZxfavhF.js → app.C8ylfBe6.js} +2 -2
- package/admin-build/_app/immutable/entry/start.CtsqDyfj.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.DlsaydXO.js → 0.CJJ6HZbp.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.D2NWN5eG.js → 1.B4sI5cB4.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.EMDaN3nw.js → 10.D6hvCer6.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.BasqQ_o9.js → 11.Dx7b8aQ5.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.DO31Ljs7.js → 12.Bqmy5KIF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.DhyAy-GZ.js → 13.CC6KpXgS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.CLecGWc4.js → 14.yCo1Ix8E.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.B9kp3W4e.js → 15.co0UfPlh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.Pu_8T3RI.js → 16.D0xkPUBW.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.DX4z43t6.js → 17.CebNqPeh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.BKsSaxrr.js → 18.JUoLOZxh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.DXNF1htN.js → 19.ND8kmQJe.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.VRVb0wee.js → 20.DYb-q3W8.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.cz3IN9Cc.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.DqZf4CtH.js → 22.UOzm8WYV.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.DtyxMiQG.js → 23.BLgq21om.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.CloWNmTd.js → 24.DN9usmUs.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.CnZWMq7_.js → 25.BddRfAyE.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.DrV7XOmf.js → 26.Dl6XHIeT.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.DV8L32OF.js → 27.D0iNwALG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.Stil2D4u.js → 28.9dKQmdGi.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.Zsm1e5Dc.js → 29.wXzfJUXp.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.CKoj2vNz.js → 3.z8ut3jS-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.Ni0k5bER.js → 30.BtZETNsL.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.mnqj9EbV.js → 31.CYonj2Jh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.B_-z9AzT.js → 4.COtDPQ9b.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.yiZ72j4k.js → 5.CTRCeIhp.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.BqykybBG.js → 6.ChHi3QkR.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.BDAHlhsF.js → 7.CCMtr6Ac.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.D8Xvy0lH.js → 8.DpWJ-X_-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.Dddmd7_F.js → 9.DOkvfmir.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/package.json +3 -3
- package/src/__tests__/admin-data-routes.test.ts +29 -0
- package/src/__tests__/d1-live-broadcast-verification.test.ts +271 -0
- package/src/__tests__/database-do-route-validation.test.ts +105 -0
- package/src/__tests__/database-live-do.test.ts +50 -0
- package/src/__tests__/database-live-emitter.test.ts +116 -1
- package/src/__tests__/database-live-route.test.ts +82 -0
- package/src/__tests__/do-router.test.ts +116 -0
- package/src/__tests__/error-format.test.ts +63 -0
- package/src/__tests__/functions-context.test.ts +674 -33
- package/src/__tests__/functions-d1-proxy.test.ts +54 -0
- package/src/__tests__/plugin-migration-routing.test.ts +32 -0
- package/src/__tests__/postgres-field-ops-compat.test.ts +110 -0
- package/src/__tests__/provider-aware-sql.test.ts +163 -0
- package/src/__tests__/room-auth-state-loss.test.ts +124 -0
- package/src/__tests__/runtime-surface-accounting.test.ts +0 -4
- package/src/__tests__/scheduled.test.ts +55 -0
- package/src/__tests__/service-key-db-proxy.test.ts +122 -1
- package/src/__tests__/sql-route.test.ts +252 -75
- package/src/__tests__/table-hook-runtime.test.ts +137 -0
- package/src/durable-objects/database-do.ts +36 -45
- package/src/durable-objects/database-live-do.ts +46 -1
- package/src/durable-objects/room-runtime-base.ts +26 -2
- package/src/durable-objects/rooms-do.ts +1 -1
- package/src/index.ts +12 -6
- package/src/lib/admin-db-target.ts +30 -74
- package/src/lib/d1-handler.ts +55 -35
- package/src/lib/database-live-emitter.ts +57 -16
- package/src/lib/do-router.ts +135 -3
- package/src/lib/functions.ts +215 -143
- package/src/lib/internal-transport.ts +28 -12
- package/src/lib/plugin-migration-routing.ts +28 -0
- package/src/lib/plugin-migrations.ts +38 -38
- package/src/lib/postgres-handler.ts +51 -31
- package/src/lib/provider-aware-sql.ts +831 -0
- package/src/lib/table-hook-runtime.ts +62 -0
- package/src/routes/admin.ts +41 -41
- package/src/routes/auth.ts +7 -2
- package/src/routes/database-live.ts +110 -12
- package/src/routes/sql.ts +64 -84
- package/src/routes/storage.ts +7 -2
- package/src/routes/tables.ts +42 -29
- package/admin-build/_app/immutable/chunks/5PDcRlfX.js +0 -1
- package/admin-build/_app/immutable/chunks/qiZXAKh-.js +0 -1
- package/admin-build/_app/immutable/entry/start.Mr9mmopc.js +0 -1
- package/admin-build/_app/immutable/nodes/21.Ck3_0D2f.js +0 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { buildFunctionContext, getWorkerUrl } from '../lib/functions.js';
|
|
3
3
|
|
|
4
|
+
function makeTaggedTemplateStrings(parts: string[]): TemplateStringsArray {
|
|
5
|
+
return Object.assign([...parts], { raw: [...parts] }) as unknown as TemplateStringsArray;
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
describe('buildFunctionContext admin.db', () => {
|
|
5
9
|
afterEach(() => {
|
|
6
10
|
vi.unstubAllGlobals();
|
|
@@ -48,6 +52,48 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
48
52
|
);
|
|
49
53
|
});
|
|
50
54
|
|
|
55
|
+
it('preserves significant whitespace in dynamic admin.db instance ids', async () => {
|
|
56
|
+
const databaseFetch = vi.fn().mockResolvedValue(
|
|
57
|
+
new Response(JSON.stringify({ items: [{ id: 'm1' }] }), {
|
|
58
|
+
status: 200,
|
|
59
|
+
headers: { 'Content-Type': 'application/json' },
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
const databaseNamespace = {
|
|
63
|
+
idFromName: vi.fn(() => 'workspace-id'),
|
|
64
|
+
get: vi.fn(() => ({ fetch: databaseFetch })),
|
|
65
|
+
} as unknown as DurableObjectNamespace;
|
|
66
|
+
|
|
67
|
+
const ctx = buildFunctionContext({
|
|
68
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
69
|
+
auth: null,
|
|
70
|
+
databaseNamespace,
|
|
71
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
72
|
+
d1Database: {} as D1Database,
|
|
73
|
+
config: {
|
|
74
|
+
databases: {
|
|
75
|
+
workspace: {
|
|
76
|
+
instance: true,
|
|
77
|
+
tables: {
|
|
78
|
+
members: { schema: { role: { type: 'string' } } },
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await ctx.admin.db('workspace', ' ws-1 ').table('members').getList();
|
|
86
|
+
|
|
87
|
+
expect(databaseNamespace.idFromName).toHaveBeenCalledWith('workspace: ws-1 ');
|
|
88
|
+
expect(databaseFetch.mock.calls[0]?.[1]).toEqual(
|
|
89
|
+
expect.objectContaining({
|
|
90
|
+
headers: expect.objectContaining({
|
|
91
|
+
'X-DO-Name': 'workspace: ws-1 ',
|
|
92
|
+
}),
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
51
97
|
it('routes upsert calls through the worker with upsert query params', async () => {
|
|
52
98
|
const fetchMock = vi.fn().mockResolvedValue(
|
|
53
99
|
new Response(JSON.stringify({ id: 'p1', title: 'Upserted', action: 'inserted' }), {
|
|
@@ -97,16 +143,19 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
97
143
|
);
|
|
98
144
|
});
|
|
99
145
|
|
|
100
|
-
it('normalizes admin.
|
|
146
|
+
it('normalizes admin.sqlProviderAware worker responses to row arrays', async () => {
|
|
101
147
|
const fetchMock = vi.fn().mockResolvedValue(
|
|
102
|
-
new Response(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
148
|
+
new Response(
|
|
149
|
+
JSON.stringify({
|
|
150
|
+
rows: [{ total: 2 }],
|
|
151
|
+
items: [{ total: 2 }],
|
|
152
|
+
results: [{ total: 2 }],
|
|
153
|
+
}),
|
|
154
|
+
{
|
|
155
|
+
status: 200,
|
|
156
|
+
headers: { 'Content-Type': 'application/json' },
|
|
157
|
+
},
|
|
158
|
+
),
|
|
110
159
|
);
|
|
111
160
|
vi.stubGlobal('fetch', fetchMock);
|
|
112
161
|
|
|
@@ -129,7 +178,11 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
129
178
|
serviceKey: 'sk-test',
|
|
130
179
|
});
|
|
131
180
|
|
|
132
|
-
const rows = await ctx.admin.
|
|
181
|
+
const rows = await ctx.admin.sqlProviderAware(
|
|
182
|
+
'shared',
|
|
183
|
+
undefined,
|
|
184
|
+
'SELECT COUNT(*) AS total FROM posts',
|
|
185
|
+
);
|
|
133
186
|
|
|
134
187
|
expect(rows).toEqual([{ total: 2 }]);
|
|
135
188
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
@@ -144,31 +197,78 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
144
197
|
);
|
|
145
198
|
});
|
|
146
199
|
|
|
200
|
+
it('rejects instance ids for single-instance admin.sqlProviderAware calls before touching direct backends', async () => {
|
|
201
|
+
const fetchMock = vi.fn();
|
|
202
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
203
|
+
|
|
204
|
+
const databaseNamespace = {
|
|
205
|
+
idFromName: vi.fn(),
|
|
206
|
+
get: vi.fn(),
|
|
207
|
+
} as unknown as DurableObjectNamespace;
|
|
208
|
+
|
|
209
|
+
const ctx = buildFunctionContext({
|
|
210
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
211
|
+
auth: null,
|
|
212
|
+
databaseNamespace,
|
|
213
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
214
|
+
d1Database: {} as D1Database,
|
|
215
|
+
config: {
|
|
216
|
+
databases: {
|
|
217
|
+
shared: {
|
|
218
|
+
tables: {
|
|
219
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
env: {
|
|
225
|
+
DB_D1_SHARED: {
|
|
226
|
+
prepare: vi.fn(),
|
|
227
|
+
},
|
|
228
|
+
} as never,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
await expect(
|
|
232
|
+
ctx.admin.sqlProviderAware('shared', 'shadow', 'SELECT COUNT(*) AS total FROM posts'),
|
|
233
|
+
).rejects.toThrow("instanceId is not allowed for single-instance namespace 'shared'");
|
|
234
|
+
expect(
|
|
235
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
236
|
+
).not.toHaveBeenCalled();
|
|
237
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
238
|
+
});
|
|
239
|
+
|
|
147
240
|
it('routes admin.sqlWithDirectD1Access through the database DO when env is available', async () => {
|
|
148
241
|
const fetchMock = vi.fn();
|
|
149
242
|
vi.stubGlobal('fetch', fetchMock);
|
|
150
243
|
|
|
151
244
|
const stub = {
|
|
152
|
-
fetch: vi
|
|
245
|
+
fetch: vi
|
|
246
|
+
.fn()
|
|
153
247
|
.mockResolvedValueOnce(
|
|
154
|
-
new Response(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
248
|
+
new Response(
|
|
249
|
+
JSON.stringify({
|
|
250
|
+
needsCreate: true,
|
|
251
|
+
namespace: 'workspace',
|
|
252
|
+
id: 'ws-1',
|
|
253
|
+
}),
|
|
254
|
+
{
|
|
255
|
+
status: 201,
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
},
|
|
258
|
+
),
|
|
162
259
|
)
|
|
163
260
|
.mockResolvedValueOnce(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
261
|
+
new Response(
|
|
262
|
+
JSON.stringify({
|
|
263
|
+
rows: [{ total: 3 }],
|
|
264
|
+
items: [{ total: 3 }],
|
|
265
|
+
results: [{ total: 3 }],
|
|
266
|
+
}),
|
|
267
|
+
{
|
|
268
|
+
status: 200,
|
|
269
|
+
headers: { 'Content-Type': 'application/json' },
|
|
270
|
+
},
|
|
271
|
+
),
|
|
172
272
|
),
|
|
173
273
|
};
|
|
174
274
|
const databaseNamespace = {
|
|
@@ -185,6 +285,7 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
185
285
|
config: {
|
|
186
286
|
databases: {
|
|
187
287
|
workspace: {
|
|
288
|
+
instance: true,
|
|
188
289
|
tables: {
|
|
189
290
|
members: { schema: { userId: { type: 'string' } } },
|
|
190
291
|
},
|
|
@@ -196,7 +297,12 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
196
297
|
serviceKey: 'sk-test',
|
|
197
298
|
});
|
|
198
299
|
|
|
199
|
-
const rows = await ctx.admin.sqlWithDirectD1Access(
|
|
300
|
+
const rows = await ctx.admin.sqlWithDirectD1Access(
|
|
301
|
+
'workspace',
|
|
302
|
+
'ws-1',
|
|
303
|
+
'SELECT COUNT(*) AS total FROM members',
|
|
304
|
+
[],
|
|
305
|
+
);
|
|
200
306
|
|
|
201
307
|
expect(rows).toEqual([{ total: 3 }]);
|
|
202
308
|
expect(stub.fetch).toHaveBeenCalledTimes(2);
|
|
@@ -213,6 +319,531 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
213
319
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
214
320
|
});
|
|
215
321
|
|
|
322
|
+
it('falls back to /api/sql for admin.db(...).table(...).sql tagged templates when only workerUrl is available', async () => {
|
|
323
|
+
const fetchMock = vi.fn().mockResolvedValue(
|
|
324
|
+
new Response(
|
|
325
|
+
JSON.stringify({
|
|
326
|
+
rows: [{ total: 2 }],
|
|
327
|
+
items: [{ total: 2 }],
|
|
328
|
+
results: [{ total: 2 }],
|
|
329
|
+
}),
|
|
330
|
+
{
|
|
331
|
+
status: 200,
|
|
332
|
+
headers: { 'Content-Type': 'application/json' },
|
|
333
|
+
},
|
|
334
|
+
),
|
|
335
|
+
);
|
|
336
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
337
|
+
|
|
338
|
+
const ctx = buildFunctionContext({
|
|
339
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
340
|
+
auth: null,
|
|
341
|
+
databaseNamespace: {} as DurableObjectNamespace,
|
|
342
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
343
|
+
d1Database: {} as D1Database,
|
|
344
|
+
config: {
|
|
345
|
+
databases: {
|
|
346
|
+
shared: {
|
|
347
|
+
tables: {
|
|
348
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
workerUrl: 'http://localhost:8787',
|
|
354
|
+
serviceKey: 'sk-test',
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const rows = await ctx.admin.db('shared').table('posts').sql`
|
|
358
|
+
SELECT COUNT(*) AS total FROM posts WHERE status = ${'published'}
|
|
359
|
+
`;
|
|
360
|
+
|
|
361
|
+
expect(rows).toEqual([{ total: 2 }]);
|
|
362
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
363
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
364
|
+
'http://localhost:8787/api/sql',
|
|
365
|
+
expect.objectContaining({ method: 'POST' }),
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('routes admin.db(...).table(...).sql tagged templates through the direct SQL executor when env is available', async () => {
|
|
370
|
+
const fetchMock = vi.fn();
|
|
371
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
372
|
+
|
|
373
|
+
const stub = {
|
|
374
|
+
fetch: vi
|
|
375
|
+
.fn()
|
|
376
|
+
.mockResolvedValueOnce(
|
|
377
|
+
new Response(
|
|
378
|
+
JSON.stringify({
|
|
379
|
+
needsCreate: true,
|
|
380
|
+
namespace: 'workspace',
|
|
381
|
+
id: 'ws-1',
|
|
382
|
+
}),
|
|
383
|
+
{
|
|
384
|
+
status: 201,
|
|
385
|
+
headers: { 'Content-Type': 'application/json' },
|
|
386
|
+
},
|
|
387
|
+
),
|
|
388
|
+
)
|
|
389
|
+
.mockResolvedValueOnce(
|
|
390
|
+
new Response(
|
|
391
|
+
JSON.stringify({
|
|
392
|
+
rows: [{ total: 5 }],
|
|
393
|
+
items: [{ total: 5 }],
|
|
394
|
+
results: [{ total: 5 }],
|
|
395
|
+
}),
|
|
396
|
+
{
|
|
397
|
+
status: 200,
|
|
398
|
+
headers: { 'Content-Type': 'application/json' },
|
|
399
|
+
},
|
|
400
|
+
),
|
|
401
|
+
),
|
|
402
|
+
};
|
|
403
|
+
const databaseNamespace = {
|
|
404
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
405
|
+
get: vi.fn().mockReturnValue(stub),
|
|
406
|
+
} as unknown as DurableObjectNamespace;
|
|
407
|
+
|
|
408
|
+
const ctx = buildFunctionContext({
|
|
409
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
410
|
+
auth: null,
|
|
411
|
+
databaseNamespace,
|
|
412
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
413
|
+
d1Database: {} as D1Database,
|
|
414
|
+
config: {
|
|
415
|
+
databases: {
|
|
416
|
+
workspace: {
|
|
417
|
+
instance: true,
|
|
418
|
+
tables: {
|
|
419
|
+
members: { schema: { userId: { type: 'string' } } },
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
env: {} as never,
|
|
425
|
+
workerUrl: 'http://localhost:8787',
|
|
426
|
+
serviceKey: 'sk-test',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const rows = await ctx.admin.db('workspace', 'ws-1').table('members').sql`
|
|
430
|
+
SELECT COUNT(*) AS total FROM members WHERE role = ${'owner'}
|
|
431
|
+
`;
|
|
432
|
+
|
|
433
|
+
expect(rows).toEqual([{ total: 5 }]);
|
|
434
|
+
expect(stub.fetch).toHaveBeenCalledTimes(2);
|
|
435
|
+
const firstRequest = stub.fetch.mock.calls[0]?.[0] as Request;
|
|
436
|
+
await expect(firstRequest.json()).resolves.toEqual({
|
|
437
|
+
query: 'SELECT COUNT(*) AS total FROM members WHERE role = ?',
|
|
438
|
+
params: ['owner'],
|
|
439
|
+
});
|
|
440
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it.each(['postgres', 'neon'] as const)(
|
|
444
|
+
'routes admin.db(...).table(...).sql tagged templates through the provider-aware direct SQL executor for %s',
|
|
445
|
+
async (provider) => {
|
|
446
|
+
const fetchMock = vi
|
|
447
|
+
.fn()
|
|
448
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
|
449
|
+
.mockResolvedValueOnce(
|
|
450
|
+
new Response(
|
|
451
|
+
JSON.stringify({
|
|
452
|
+
columns: ['literal', 'total'],
|
|
453
|
+
rows: [{ literal: '?', total: 5 }],
|
|
454
|
+
rowCount: 1,
|
|
455
|
+
}),
|
|
456
|
+
{
|
|
457
|
+
status: 200,
|
|
458
|
+
headers: { 'Content-Type': 'application/json' },
|
|
459
|
+
},
|
|
460
|
+
),
|
|
461
|
+
);
|
|
462
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
463
|
+
|
|
464
|
+
const databaseNamespace = {
|
|
465
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
466
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
467
|
+
} as unknown as DurableObjectNamespace;
|
|
468
|
+
|
|
469
|
+
const ctx = buildFunctionContext({
|
|
470
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
471
|
+
auth: null,
|
|
472
|
+
databaseNamespace,
|
|
473
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
474
|
+
d1Database: {} as D1Database,
|
|
475
|
+
config: {
|
|
476
|
+
databases: {
|
|
477
|
+
shared: {
|
|
478
|
+
provider,
|
|
479
|
+
tables: {
|
|
480
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
env: {
|
|
486
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
487
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
488
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
489
|
+
} as never,
|
|
490
|
+
workerUrl: 'http://localhost:8787',
|
|
491
|
+
serviceKey: 'sk-test',
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const rows = await ctx.admin.db('shared').table('posts').sql`
|
|
495
|
+
SELECT '?' AS literal, COUNT(*) AS total FROM posts WHERE title = ${'owner'}
|
|
496
|
+
`;
|
|
497
|
+
|
|
498
|
+
expect(rows).toEqual([{ literal: '?', total: 5 }]);
|
|
499
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
500
|
+
expect(fetchMock.mock.calls[1]?.[0]).toBe('http://127.0.0.1:8788/postgres/query');
|
|
501
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body ?? '{}'))).toEqual({
|
|
502
|
+
namespace: 'shared',
|
|
503
|
+
sql: "SELECT '?' AS literal, COUNT(*) AS total FROM posts WHERE title = $1",
|
|
504
|
+
params: ['owner'],
|
|
505
|
+
});
|
|
506
|
+
expect(
|
|
507
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
508
|
+
).not.toHaveBeenCalled();
|
|
509
|
+
},
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
it.each(['postgres', 'neon'] as const)(
|
|
513
|
+
'preserves PostgreSQL @? operators when admin.db(...).table(...).sql uses the provider-aware direct SQL executor for %s',
|
|
514
|
+
async (provider) => {
|
|
515
|
+
const fetchMock = vi
|
|
516
|
+
.fn()
|
|
517
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
|
518
|
+
.mockResolvedValueOnce(
|
|
519
|
+
new Response(
|
|
520
|
+
JSON.stringify({
|
|
521
|
+
columns: ['total'],
|
|
522
|
+
rows: [{ total: 4 }],
|
|
523
|
+
rowCount: 1,
|
|
524
|
+
}),
|
|
525
|
+
{
|
|
526
|
+
status: 200,
|
|
527
|
+
headers: { 'Content-Type': 'application/json' },
|
|
528
|
+
},
|
|
529
|
+
),
|
|
530
|
+
);
|
|
531
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
532
|
+
|
|
533
|
+
const databaseNamespace = {
|
|
534
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
535
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
536
|
+
} as unknown as DurableObjectNamespace;
|
|
537
|
+
|
|
538
|
+
const ctx = buildFunctionContext({
|
|
539
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
540
|
+
auth: null,
|
|
541
|
+
databaseNamespace,
|
|
542
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
543
|
+
d1Database: {} as D1Database,
|
|
544
|
+
config: {
|
|
545
|
+
databases: {
|
|
546
|
+
shared: {
|
|
547
|
+
provider,
|
|
548
|
+
tables: {
|
|
549
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
env: {
|
|
555
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
556
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
557
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
558
|
+
} as never,
|
|
559
|
+
workerUrl: 'http://localhost:8787',
|
|
560
|
+
serviceKey: 'sk-test',
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const rows = await ctx.admin.db('shared').table('posts').sql`
|
|
564
|
+
SELECT COUNT(*) AS total
|
|
565
|
+
FROM posts
|
|
566
|
+
WHERE metadata @? '$.featured'
|
|
567
|
+
AND title = ${'owner'}
|
|
568
|
+
`;
|
|
569
|
+
|
|
570
|
+
expect(rows).toEqual([{ total: 4 }]);
|
|
571
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body ?? '{}'))).toEqual({
|
|
572
|
+
namespace: 'shared',
|
|
573
|
+
sql: "SELECT COUNT(*) AS total\n FROM posts\n WHERE metadata @? '$.featured'\n AND title = $1",
|
|
574
|
+
params: ['owner'],
|
|
575
|
+
});
|
|
576
|
+
expect(
|
|
577
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
578
|
+
).not.toHaveBeenCalled();
|
|
579
|
+
},
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
it.each(['postgres', 'neon'] as const)(
|
|
583
|
+
'unescapes PostgreSQL @\\? operators when admin.db(...).table(...).sql uses tagged-template markers for %s',
|
|
584
|
+
async (provider) => {
|
|
585
|
+
const fetchMock = vi
|
|
586
|
+
.fn()
|
|
587
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
|
588
|
+
.mockResolvedValueOnce(
|
|
589
|
+
new Response(
|
|
590
|
+
JSON.stringify({
|
|
591
|
+
columns: ['total'],
|
|
592
|
+
rows: [{ total: 9 }],
|
|
593
|
+
rowCount: 1,
|
|
594
|
+
}),
|
|
595
|
+
{
|
|
596
|
+
status: 200,
|
|
597
|
+
headers: { 'Content-Type': 'application/json' },
|
|
598
|
+
},
|
|
599
|
+
),
|
|
600
|
+
);
|
|
601
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
602
|
+
|
|
603
|
+
const databaseNamespace = {
|
|
604
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
605
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
606
|
+
} as unknown as DurableObjectNamespace;
|
|
607
|
+
|
|
608
|
+
const ctx = buildFunctionContext({
|
|
609
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
610
|
+
auth: null,
|
|
611
|
+
databaseNamespace,
|
|
612
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
613
|
+
d1Database: {} as D1Database,
|
|
614
|
+
config: {
|
|
615
|
+
databases: {
|
|
616
|
+
shared: {
|
|
617
|
+
provider,
|
|
618
|
+
tables: {
|
|
619
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
env: {
|
|
625
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
626
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
627
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
628
|
+
} as never,
|
|
629
|
+
workerUrl: 'http://localhost:8787',
|
|
630
|
+
serviceKey: 'sk-test',
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const rows = await ctx.admin.db('shared').table('posts').sql(
|
|
634
|
+
makeTaggedTemplateStrings([
|
|
635
|
+
"SELECT COUNT(*) AS total FROM posts WHERE metadata @\\? '$.featured' AND title = ",
|
|
636
|
+
'',
|
|
637
|
+
]),
|
|
638
|
+
'owner',
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
expect(rows).toEqual([{ total: 9 }]);
|
|
642
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body ?? '{}'))).toEqual({
|
|
643
|
+
namespace: 'shared',
|
|
644
|
+
sql: "SELECT COUNT(*) AS total FROM posts WHERE metadata @? '$.featured' AND title = $1",
|
|
645
|
+
params: ['owner'],
|
|
646
|
+
});
|
|
647
|
+
expect(
|
|
648
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
649
|
+
).not.toHaveBeenCalled();
|
|
650
|
+
},
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
it.each(['postgres', 'neon'] as const)(
|
|
654
|
+
'rejects admin.db(...).table(...).sql tagged templates that mix interpolation with literal $n placeholders for %s',
|
|
655
|
+
async (provider) => {
|
|
656
|
+
const fetchMock = vi.fn();
|
|
657
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
658
|
+
|
|
659
|
+
const databaseNamespace = {
|
|
660
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
661
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
662
|
+
} as unknown as DurableObjectNamespace;
|
|
663
|
+
|
|
664
|
+
const ctx = buildFunctionContext({
|
|
665
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
666
|
+
auth: null,
|
|
667
|
+
databaseNamespace,
|
|
668
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
669
|
+
d1Database: {} as D1Database,
|
|
670
|
+
config: {
|
|
671
|
+
databases: {
|
|
672
|
+
shared: {
|
|
673
|
+
provider,
|
|
674
|
+
tables: {
|
|
675
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
env: {
|
|
681
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
682
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
683
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
684
|
+
} as never,
|
|
685
|
+
workerUrl: 'http://localhost:8787',
|
|
686
|
+
serviceKey: 'sk-test',
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
await expect(
|
|
690
|
+
ctx.admin.db('shared').table('posts').sql(
|
|
691
|
+
makeTaggedTemplateStrings([
|
|
692
|
+
'SELECT COUNT(*) AS total FROM posts WHERE tenant_id = $1 AND title = ',
|
|
693
|
+
'',
|
|
694
|
+
]),
|
|
695
|
+
'owner',
|
|
696
|
+
),
|
|
697
|
+
).rejects.toThrow(
|
|
698
|
+
'Cannot mix tagged template interpolation with PostgreSQL-style $n placeholders.',
|
|
699
|
+
);
|
|
700
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
701
|
+
expect(
|
|
702
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
703
|
+
).not.toHaveBeenCalled();
|
|
704
|
+
},
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
it.each(['postgres', 'neon'] as const)(
|
|
708
|
+
'routes admin.sqlProviderAware through the provider-aware direct SQL executor for %s',
|
|
709
|
+
async (provider) => {
|
|
710
|
+
const fetchMock = vi
|
|
711
|
+
.fn()
|
|
712
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
|
713
|
+
.mockResolvedValueOnce(
|
|
714
|
+
new Response(
|
|
715
|
+
JSON.stringify({
|
|
716
|
+
columns: ['total'],
|
|
717
|
+
rows: [{ total: 7 }],
|
|
718
|
+
rowCount: 1,
|
|
719
|
+
}),
|
|
720
|
+
{
|
|
721
|
+
status: 200,
|
|
722
|
+
headers: { 'Content-Type': 'application/json' },
|
|
723
|
+
},
|
|
724
|
+
),
|
|
725
|
+
);
|
|
726
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
727
|
+
|
|
728
|
+
const databaseNamespace = {
|
|
729
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
730
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
731
|
+
} as unknown as DurableObjectNamespace;
|
|
732
|
+
|
|
733
|
+
const ctx = buildFunctionContext({
|
|
734
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
735
|
+
auth: null,
|
|
736
|
+
databaseNamespace,
|
|
737
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
738
|
+
d1Database: {} as D1Database,
|
|
739
|
+
config: {
|
|
740
|
+
databases: {
|
|
741
|
+
shared: {
|
|
742
|
+
provider,
|
|
743
|
+
tables: {
|
|
744
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
env: {
|
|
750
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
751
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
752
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
753
|
+
} as never,
|
|
754
|
+
workerUrl: 'http://localhost:8787',
|
|
755
|
+
serviceKey: 'sk-test',
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const rows = await ctx.admin.sqlProviderAware(
|
|
759
|
+
'shared',
|
|
760
|
+
undefined,
|
|
761
|
+
'SELECT COUNT(*) AS total FROM posts WHERE title = ?',
|
|
762
|
+
['owner'],
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
expect(rows).toEqual([{ total: 7 }]);
|
|
766
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body ?? '{}'))).toEqual({
|
|
767
|
+
namespace: 'shared',
|
|
768
|
+
sql: 'SELECT COUNT(*) AS total FROM posts WHERE title = $1',
|
|
769
|
+
params: ['owner'],
|
|
770
|
+
});
|
|
771
|
+
expect(
|
|
772
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
773
|
+
).not.toHaveBeenCalled();
|
|
774
|
+
},
|
|
775
|
+
);
|
|
776
|
+
|
|
777
|
+
it.each(['postgres', 'neon'] as const)(
|
|
778
|
+
'preserves PostgreSQL @? operators when admin.sqlProviderAware uses the provider-aware direct SQL executor for %s',
|
|
779
|
+
async (provider) => {
|
|
780
|
+
const fetchMock = vi
|
|
781
|
+
.fn()
|
|
782
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }))
|
|
783
|
+
.mockResolvedValueOnce(
|
|
784
|
+
new Response(
|
|
785
|
+
JSON.stringify({
|
|
786
|
+
columns: ['total'],
|
|
787
|
+
rows: [{ total: 6 }],
|
|
788
|
+
rowCount: 1,
|
|
789
|
+
}),
|
|
790
|
+
{
|
|
791
|
+
status: 200,
|
|
792
|
+
headers: { 'Content-Type': 'application/json' },
|
|
793
|
+
},
|
|
794
|
+
),
|
|
795
|
+
);
|
|
796
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
797
|
+
|
|
798
|
+
const databaseNamespace = {
|
|
799
|
+
idFromName: vi.fn().mockReturnValue('do-id'),
|
|
800
|
+
get: vi.fn(() => ({ fetch: vi.fn() })),
|
|
801
|
+
} as unknown as DurableObjectNamespace;
|
|
802
|
+
|
|
803
|
+
const ctx = buildFunctionContext({
|
|
804
|
+
request: new Request('http://localhost/api/functions/feed-summary'),
|
|
805
|
+
auth: null,
|
|
806
|
+
databaseNamespace,
|
|
807
|
+
authNamespace: {} as DurableObjectNamespace,
|
|
808
|
+
d1Database: {} as D1Database,
|
|
809
|
+
config: {
|
|
810
|
+
databases: {
|
|
811
|
+
shared: {
|
|
812
|
+
provider,
|
|
813
|
+
tables: {
|
|
814
|
+
posts: { schema: { title: { type: 'string' } } },
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
},
|
|
819
|
+
env: {
|
|
820
|
+
EDGEBASE_DEV_SIDECAR_PORT: '8788',
|
|
821
|
+
JWT_ADMIN_SECRET: 'jwt-secret',
|
|
822
|
+
DB_POSTGRES_SHARED_URL: 'postgres://edgebase:test@localhost/shared',
|
|
823
|
+
} as never,
|
|
824
|
+
workerUrl: 'http://localhost:8787',
|
|
825
|
+
serviceKey: 'sk-test',
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const rows = await ctx.admin.sqlProviderAware(
|
|
829
|
+
'shared',
|
|
830
|
+
undefined,
|
|
831
|
+
"SELECT COUNT(*) AS total FROM posts WHERE metadata @? '$.featured' AND title = ?",
|
|
832
|
+
['owner'],
|
|
833
|
+
);
|
|
834
|
+
|
|
835
|
+
expect(rows).toEqual([{ total: 6 }]);
|
|
836
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body ?? '{}'))).toEqual({
|
|
837
|
+
namespace: 'shared',
|
|
838
|
+
sql: "SELECT COUNT(*) AS total FROM posts WHERE metadata @? '$.featured' AND title = $1",
|
|
839
|
+
params: ['owner'],
|
|
840
|
+
});
|
|
841
|
+
expect(
|
|
842
|
+
(databaseNamespace as unknown as { get: ReturnType<typeof vi.fn> }).get,
|
|
843
|
+
).not.toHaveBeenCalled();
|
|
844
|
+
},
|
|
845
|
+
);
|
|
846
|
+
|
|
216
847
|
it('routes admin.kv through the configured KV binding when env is available', async () => {
|
|
217
848
|
const fetchMock = vi.fn();
|
|
218
849
|
vi.stubGlobal('fetch', fetchMock);
|
|
@@ -285,10 +916,14 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
285
916
|
serviceKey: 'sk-test',
|
|
286
917
|
});
|
|
287
918
|
|
|
288
|
-
const rows = await ctx.admin
|
|
919
|
+
const rows = await ctx.admin
|
|
920
|
+
.d1('analytics')
|
|
921
|
+
.exec('SELECT COUNT(*) AS total FROM rollups WHERE runId = ?', ['r1']);
|
|
289
922
|
|
|
290
923
|
expect(rows).toEqual([{ total: 4 }]);
|
|
291
|
-
expect(d1Binding.prepare).toHaveBeenCalledWith(
|
|
924
|
+
expect(d1Binding.prepare).toHaveBeenCalledWith(
|
|
925
|
+
'SELECT COUNT(*) AS total FROM rollups WHERE runId = ?',
|
|
926
|
+
);
|
|
292
927
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
293
928
|
});
|
|
294
929
|
|
|
@@ -306,7 +941,9 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
306
941
|
};
|
|
307
942
|
|
|
308
943
|
const ctx = buildFunctionContext({
|
|
309
|
-
request: new Request(
|
|
944
|
+
request: new Request(
|
|
945
|
+
'http://localhost/api/functions/mock/email/inbox/user@test.edgebase.fun',
|
|
946
|
+
),
|
|
310
947
|
auth: null,
|
|
311
948
|
databaseNamespace: {} as DurableObjectNamespace,
|
|
312
949
|
authNamespace: {} as DurableObjectNamespace,
|
|
@@ -317,10 +954,14 @@ describe('buildFunctionContext admin.db', () => {
|
|
|
317
954
|
serviceKey: 'sk-test',
|
|
318
955
|
});
|
|
319
956
|
|
|
320
|
-
const rows = await ctx.admin
|
|
957
|
+
const rows = await ctx.admin
|
|
958
|
+
.d1('auth')
|
|
959
|
+
.exec('SELECT token FROM _email_tokens WHERE userId = ?', ['u1']);
|
|
321
960
|
|
|
322
961
|
expect(rows).toEqual([{ token: 'tok-1' }]);
|
|
323
|
-
expect(authBinding.prepare).toHaveBeenCalledWith(
|
|
962
|
+
expect(authBinding.prepare).toHaveBeenCalledWith(
|
|
963
|
+
'SELECT token FROM _email_tokens WHERE userId = ?',
|
|
964
|
+
);
|
|
324
965
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
325
966
|
});
|
|
326
967
|
|