ai-database 2.0.2 → 2.1.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 +36 -0
- package/dist/actions.d.ts +247 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +260 -0
- package/dist/actions.js.map +1 -0
- package/dist/ai-promise-db.d.ts +34 -2
- package/dist/ai-promise-db.d.ts.map +1 -1
- package/dist/ai-promise-db.js +511 -66
- package/dist/ai-promise-db.js.map +1 -1
- package/dist/constants.d.ts +16 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -0
- package/dist/events.d.ts +153 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +154 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/memory-provider.d.ts +144 -2
- package/dist/memory-provider.d.ts.map +1 -1
- package/dist/memory-provider.js +569 -13
- package/dist/memory-provider.js.map +1 -1
- package/dist/schema/cascade.d.ts +96 -0
- package/dist/schema/cascade.d.ts.map +1 -0
- package/dist/schema/cascade.js +528 -0
- package/dist/schema/cascade.js.map +1 -0
- package/dist/schema/index.d.ts +197 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +1211 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/parse.d.ts +225 -0
- package/dist/schema/parse.d.ts.map +1 -0
- package/dist/schema/parse.js +732 -0
- package/dist/schema/parse.js.map +1 -0
- package/dist/schema/provider.d.ts +176 -0
- package/dist/schema/provider.d.ts.map +1 -0
- package/dist/schema/provider.js +258 -0
- package/dist/schema/provider.js.map +1 -0
- package/dist/schema/resolve.d.ts +87 -0
- package/dist/schema/resolve.d.ts.map +1 -0
- package/dist/schema/resolve.js +474 -0
- package/dist/schema/resolve.js.map +1 -0
- package/dist/schema/semantic.d.ts +53 -0
- package/dist/schema/semantic.d.ts.map +1 -0
- package/dist/schema/semantic.js +247 -0
- package/dist/schema/semantic.js.map +1 -0
- package/dist/schema/types.d.ts +528 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +9 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema.d.ts +24 -867
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +41 -1124
- package/dist/schema.js.map +1 -1
- package/dist/semantic.d.ts +175 -0
- package/dist/semantic.d.ts.map +1 -0
- package/dist/semantic.js +338 -0
- package/dist/semantic.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +13 -4
- package/.turbo/turbo-build.log +0 -5
- package/TESTING.md +0 -410
- package/TEST_SUMMARY.md +0 -250
- package/TODO.md +0 -128
- package/src/ai-promise-db.ts +0 -1243
- package/src/authorization.ts +0 -1102
- package/src/durable-clickhouse.ts +0 -596
- package/src/durable-promise.ts +0 -582
- package/src/execution-queue.ts +0 -608
- package/src/index.test.ts +0 -868
- package/src/index.ts +0 -337
- package/src/linguistic.ts +0 -404
- package/src/memory-provider.test.ts +0 -1036
- package/src/memory-provider.ts +0 -1119
- package/src/schema.test.ts +0 -1254
- package/src/schema.ts +0 -2296
- package/src/tests.ts +0 -725
- package/src/types.ts +0 -1177
- package/test/README.md +0 -153
- package/test/edge-cases.test.ts +0 -646
- package/test/provider-resolution.test.ts +0 -402
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -19
package/test/edge-cases.test.ts
DELETED
|
@@ -1,646 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for edge cases and error handling
|
|
3
|
-
*
|
|
4
|
-
* Covers unusual inputs, boundary conditions, and error scenarios.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
8
|
-
import { DB, setProvider, createMemoryProvider } from '../src/index.js'
|
|
9
|
-
import type { DatabaseSchema } from '../src/index.js'
|
|
10
|
-
|
|
11
|
-
describe('edge cases', () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
setProvider(createMemoryProvider())
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('empty and minimal schemas', () => {
|
|
17
|
-
it('handles empty schema', () => {
|
|
18
|
-
const schema: DatabaseSchema = {}
|
|
19
|
-
|
|
20
|
-
const { db } = DB(schema)
|
|
21
|
-
|
|
22
|
-
expect(db.$schema.entities.size).toBe(0)
|
|
23
|
-
expect(typeof db.get).toBe('function')
|
|
24
|
-
expect(typeof db.search).toBe('function')
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('handles entity with no fields', () => {
|
|
28
|
-
const schema: DatabaseSchema = {
|
|
29
|
-
Empty: {},
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const { db } = DB(schema)
|
|
33
|
-
|
|
34
|
-
expect(db.Empty).toBeDefined()
|
|
35
|
-
expect(typeof db.Empty.create).toBe('function')
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('creates entity with no data fields', async () => {
|
|
39
|
-
const schema: DatabaseSchema = {
|
|
40
|
-
Marker: {},
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const { db } = DB(schema)
|
|
44
|
-
|
|
45
|
-
const marker = await db.Marker.create('mark1', {})
|
|
46
|
-
|
|
47
|
-
expect(marker.$id).toBe('mark1')
|
|
48
|
-
expect(marker.$type).toBe('Marker')
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
describe('special characters in IDs', () => {
|
|
53
|
-
const schema = {
|
|
54
|
-
User: { name: 'string' },
|
|
55
|
-
} as const
|
|
56
|
-
|
|
57
|
-
it('handles IDs with hyphens', async () => {
|
|
58
|
-
const { db } = DB(schema)
|
|
59
|
-
|
|
60
|
-
const user = await db.User.create('user-123', { name: 'Test' })
|
|
61
|
-
expect(user.$id).toBe('user-123')
|
|
62
|
-
|
|
63
|
-
const retrieved = await db.User.get('user-123')
|
|
64
|
-
expect(retrieved?.$id).toBe('user-123')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('handles IDs with underscores', async () => {
|
|
68
|
-
const { db } = DB(schema)
|
|
69
|
-
|
|
70
|
-
const user = await db.User.create('user_123', { name: 'Test' })
|
|
71
|
-
expect(user.$id).toBe('user_123')
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('handles IDs with dots', async () => {
|
|
75
|
-
const { db } = DB(schema)
|
|
76
|
-
|
|
77
|
-
const user = await db.User.create('user.123', { name: 'Test' })
|
|
78
|
-
expect(user.$id).toBe('user.123')
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('handles IDs with slashes (path-like)', async () => {
|
|
82
|
-
const { db } = DB(schema)
|
|
83
|
-
|
|
84
|
-
const user = await db.User.create('org/user/123', { name: 'Test' })
|
|
85
|
-
expect(user.$id).toBe('org/user/123')
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('handles UUID-style IDs', async () => {
|
|
89
|
-
const { db } = DB(schema)
|
|
90
|
-
|
|
91
|
-
const uuid = '550e8400-e29b-41d4-a716-446655440000'
|
|
92
|
-
const user = await db.User.create(uuid, { name: 'Test' })
|
|
93
|
-
expect(user.$id).toBe(uuid)
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe('special characters in data', () => {
|
|
98
|
-
const schema = {
|
|
99
|
-
User: {
|
|
100
|
-
name: 'string',
|
|
101
|
-
bio: 'string?',
|
|
102
|
-
},
|
|
103
|
-
} as const
|
|
104
|
-
|
|
105
|
-
it('handles unicode characters', async () => {
|
|
106
|
-
const { db } = DB(schema)
|
|
107
|
-
|
|
108
|
-
const user = await db.User.create('user1', {
|
|
109
|
-
name: '日本語',
|
|
110
|
-
bio: '🚀 Emoji test',
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
expect(user.name).toBe('日本語')
|
|
114
|
-
expect(user.bio).toBe('🚀 Emoji test')
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('handles newlines in data', async () => {
|
|
118
|
-
const { db } = DB(schema)
|
|
119
|
-
|
|
120
|
-
const user = await db.User.create('user1', {
|
|
121
|
-
name: 'John',
|
|
122
|
-
bio: 'Line 1\nLine 2\nLine 3',
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
expect(user.bio).toContain('\n')
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('handles special JSON characters', async () => {
|
|
129
|
-
const { db } = DB(schema)
|
|
130
|
-
|
|
131
|
-
const user = await db.User.create('user1', {
|
|
132
|
-
name: 'Test "quoted" name',
|
|
133
|
-
bio: 'Backslash: \\ and more: \t\n',
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
expect(user.name).toContain('"')
|
|
137
|
-
expect(user.bio).toContain('\\')
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
describe('large data', () => {
|
|
142
|
-
const schema = {
|
|
143
|
-
Document: {
|
|
144
|
-
title: 'string',
|
|
145
|
-
content: 'markdown',
|
|
146
|
-
},
|
|
147
|
-
} as const
|
|
148
|
-
|
|
149
|
-
it('handles large strings', async () => {
|
|
150
|
-
const { db } = DB(schema)
|
|
151
|
-
|
|
152
|
-
const largeContent = 'x'.repeat(100000) // 100KB string
|
|
153
|
-
|
|
154
|
-
const doc = await db.Document.create('doc1', {
|
|
155
|
-
title: 'Large',
|
|
156
|
-
content: largeContent,
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
expect(doc.content.length).toBe(100000)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it('handles many entities', async () => {
|
|
163
|
-
const { db } = DB(schema)
|
|
164
|
-
|
|
165
|
-
const count = 1000
|
|
166
|
-
const promises = []
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < count; i++) {
|
|
169
|
-
promises.push(
|
|
170
|
-
db.Document.create(`doc${i}`, {
|
|
171
|
-
title: `Document ${i}`,
|
|
172
|
-
content: `Content ${i}`,
|
|
173
|
-
})
|
|
174
|
-
)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
await Promise.all(promises)
|
|
178
|
-
|
|
179
|
-
const docs = await db.Document.list()
|
|
180
|
-
expect(docs.length).toBe(count)
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
it('handles large result sets', async () => {
|
|
184
|
-
const { db } = DB(schema)
|
|
185
|
-
|
|
186
|
-
// Create 100 documents
|
|
187
|
-
for (let i = 0; i < 100; i++) {
|
|
188
|
-
await db.Document.create(`doc${i}`, {
|
|
189
|
-
title: `Document ${i}`,
|
|
190
|
-
content: `Content for document ${i}`,
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const all = await db.Document.list()
|
|
195
|
-
expect(all.length).toBe(100)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('concurrent operations', () => {
|
|
200
|
-
const schema = {
|
|
201
|
-
Counter: {
|
|
202
|
-
value: 'number',
|
|
203
|
-
},
|
|
204
|
-
} as const
|
|
205
|
-
|
|
206
|
-
it('handles concurrent creates', async () => {
|
|
207
|
-
const { db } = DB(schema)
|
|
208
|
-
|
|
209
|
-
const promises = []
|
|
210
|
-
for (let i = 0; i < 10; i++) {
|
|
211
|
-
promises.push(
|
|
212
|
-
db.Counter.create(`counter${i}`, { value: i })
|
|
213
|
-
)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const results = await Promise.all(promises)
|
|
217
|
-
|
|
218
|
-
expect(results).toHaveLength(10)
|
|
219
|
-
expect(new Set(results.map(r => r.$id)).size).toBe(10)
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
it('handles concurrent updates', async () => {
|
|
223
|
-
const { db } = DB(schema)
|
|
224
|
-
|
|
225
|
-
await db.Counter.create('counter1', { value: 0 })
|
|
226
|
-
|
|
227
|
-
const promises = []
|
|
228
|
-
for (let i = 1; i <= 5; i++) {
|
|
229
|
-
promises.push(
|
|
230
|
-
db.Counter.update('counter1', { value: i })
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
await Promise.all(promises)
|
|
235
|
-
|
|
236
|
-
const counter = await db.Counter.get('counter1')
|
|
237
|
-
expect(counter?.value).toBeDefined()
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('handles concurrent list operations', async () => {
|
|
241
|
-
const { db } = DB(schema)
|
|
242
|
-
|
|
243
|
-
await db.Counter.create('counter1', { value: 1 })
|
|
244
|
-
await db.Counter.create('counter2', { value: 2 })
|
|
245
|
-
|
|
246
|
-
const promises = []
|
|
247
|
-
for (let i = 0; i < 10; i++) {
|
|
248
|
-
promises.push(db.Counter.list())
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const results = await Promise.all(promises)
|
|
252
|
-
|
|
253
|
-
results.forEach(result => {
|
|
254
|
-
expect(result.length).toBe(2)
|
|
255
|
-
})
|
|
256
|
-
})
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
describe('optional fields', () => {
|
|
260
|
-
const schema = {
|
|
261
|
-
User: {
|
|
262
|
-
name: 'string',
|
|
263
|
-
email: 'string?',
|
|
264
|
-
age: 'number?',
|
|
265
|
-
bio: 'string?',
|
|
266
|
-
},
|
|
267
|
-
} as const
|
|
268
|
-
|
|
269
|
-
it('creates entity with missing optional fields', async () => {
|
|
270
|
-
const { db } = DB(schema)
|
|
271
|
-
|
|
272
|
-
const user = await db.User.create('user1', {
|
|
273
|
-
name: 'John',
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
expect(user.name).toBe('John')
|
|
277
|
-
expect(user.email).toBeUndefined()
|
|
278
|
-
expect(user.age).toBeUndefined()
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
it('creates entity with some optional fields', async () => {
|
|
282
|
-
const { db } = DB(schema)
|
|
283
|
-
|
|
284
|
-
const user = await db.User.create('user1', {
|
|
285
|
-
name: 'John',
|
|
286
|
-
email: 'john@example.com',
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
expect(user.email).toBe('john@example.com')
|
|
290
|
-
expect(user.age).toBeUndefined()
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
it('updates to set optional field', async () => {
|
|
294
|
-
const { db } = DB(schema)
|
|
295
|
-
|
|
296
|
-
await db.User.create('user1', { name: 'John' })
|
|
297
|
-
|
|
298
|
-
const updated = await db.User.update('user1', {
|
|
299
|
-
email: 'john@example.com',
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
expect(updated.email).toBe('john@example.com')
|
|
303
|
-
})
|
|
304
|
-
|
|
305
|
-
it('updates to unset optional field (set to undefined)', async () => {
|
|
306
|
-
const { db } = DB(schema)
|
|
307
|
-
|
|
308
|
-
await db.User.create('user1', {
|
|
309
|
-
name: 'John',
|
|
310
|
-
email: 'john@example.com',
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
const updated = await db.User.update('user1', {
|
|
314
|
-
email: undefined,
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
expect(updated.email).toBeUndefined()
|
|
318
|
-
})
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
describe('array fields', () => {
|
|
322
|
-
const schema = {
|
|
323
|
-
Post: {
|
|
324
|
-
title: 'string',
|
|
325
|
-
tags: 'string[]',
|
|
326
|
-
scores: 'number[]',
|
|
327
|
-
},
|
|
328
|
-
} as const
|
|
329
|
-
|
|
330
|
-
it('creates entity with empty arrays', async () => {
|
|
331
|
-
const { db } = DB(schema)
|
|
332
|
-
|
|
333
|
-
const post = await db.Post.create('post1', {
|
|
334
|
-
title: 'Test',
|
|
335
|
-
tags: [],
|
|
336
|
-
scores: [],
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
expect(post.tags).toEqual([])
|
|
340
|
-
expect(post.scores).toEqual([])
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
it('creates entity with array values', async () => {
|
|
344
|
-
const { db } = DB(schema)
|
|
345
|
-
|
|
346
|
-
const post = await db.Post.create('post1', {
|
|
347
|
-
title: 'Test',
|
|
348
|
-
tags: ['typescript', 'javascript'],
|
|
349
|
-
scores: [1, 2, 3],
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
expect(post.tags).toEqual(['typescript', 'javascript'])
|
|
353
|
-
expect(post.scores).toEqual([1, 2, 3])
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
it('updates array fields', async () => {
|
|
357
|
-
const { db } = DB(schema)
|
|
358
|
-
|
|
359
|
-
await db.Post.create('post1', {
|
|
360
|
-
title: 'Test',
|
|
361
|
-
tags: ['old'],
|
|
362
|
-
scores: [1],
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
const updated = await db.Post.update('post1', {
|
|
366
|
-
tags: ['new', 'tags'],
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
expect(updated.tags).toEqual(['new', 'tags'])
|
|
370
|
-
})
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
describe('URL parsing edge cases', () => {
|
|
374
|
-
const schema = {
|
|
375
|
-
User: { name: 'string' },
|
|
376
|
-
} as const
|
|
377
|
-
|
|
378
|
-
it('parses full HTTPS URL', async () => {
|
|
379
|
-
const { db } = DB(schema)
|
|
380
|
-
|
|
381
|
-
await db.User.create('john', { name: 'John' })
|
|
382
|
-
|
|
383
|
-
const user = await db.get('https://example.com/User/john')
|
|
384
|
-
expect(user).toBeDefined()
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it('parses HTTP URL', async () => {
|
|
388
|
-
const { db } = DB(schema)
|
|
389
|
-
|
|
390
|
-
await db.User.create('john', { name: 'John' })
|
|
391
|
-
|
|
392
|
-
const user = await db.get('http://example.com/User/john')
|
|
393
|
-
expect(user).toBeDefined()
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
it('parses type/id path', async () => {
|
|
397
|
-
const { db } = DB(schema)
|
|
398
|
-
|
|
399
|
-
await db.User.create('john', { name: 'John' })
|
|
400
|
-
|
|
401
|
-
const user = await db.get('User/john')
|
|
402
|
-
expect(user).toBeDefined()
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
it('parses nested path IDs', async () => {
|
|
406
|
-
const { db } = DB(schema)
|
|
407
|
-
|
|
408
|
-
await db.User.create('org/team/john', { name: 'John' })
|
|
409
|
-
|
|
410
|
-
const user = await db.get('User/org/team/john')
|
|
411
|
-
expect(user).toBeDefined()
|
|
412
|
-
})
|
|
413
|
-
|
|
414
|
-
it('handles URL with query parameters', async () => {
|
|
415
|
-
const { db } = DB(schema)
|
|
416
|
-
|
|
417
|
-
await db.User.create('john', { name: 'John' })
|
|
418
|
-
|
|
419
|
-
// Should ignore query params
|
|
420
|
-
const user = await db.get('https://example.com/User/john?param=value')
|
|
421
|
-
expect(user).toBeDefined()
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
it('handles URL with hash', async () => {
|
|
425
|
-
const { db } = DB(schema)
|
|
426
|
-
|
|
427
|
-
await db.User.create('john', { name: 'John' })
|
|
428
|
-
|
|
429
|
-
// Should ignore hash
|
|
430
|
-
const user = await db.get('https://example.com/User/john#section')
|
|
431
|
-
expect(user).toBeDefined()
|
|
432
|
-
})
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
describe('relation edge cases', () => {
|
|
436
|
-
const schema = {
|
|
437
|
-
User: {
|
|
438
|
-
name: 'string',
|
|
439
|
-
manager: 'User.reports?',
|
|
440
|
-
},
|
|
441
|
-
} as const
|
|
442
|
-
|
|
443
|
-
it('handles self-referential optional relation', async () => {
|
|
444
|
-
const { db } = DB(schema)
|
|
445
|
-
|
|
446
|
-
const user = await db.User.create('john', {
|
|
447
|
-
name: 'John',
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
// Verify the user was created correctly
|
|
451
|
-
expect(user.$id).toBe('john')
|
|
452
|
-
expect(user.name).toBe('John')
|
|
453
|
-
})
|
|
454
|
-
|
|
455
|
-
it('handles circular relations through provider', async () => {
|
|
456
|
-
const { db } = DB(schema)
|
|
457
|
-
const provider = createMemoryProvider()
|
|
458
|
-
setProvider(provider)
|
|
459
|
-
|
|
460
|
-
await db.User.create('alice', { name: 'Alice' })
|
|
461
|
-
await db.User.create('bob', { name: 'Bob' })
|
|
462
|
-
|
|
463
|
-
// Alice manages Bob, Bob manages Alice (circular)
|
|
464
|
-
await provider.relate('User', 'alice', 'reports', 'User', 'bob')
|
|
465
|
-
await provider.relate('User', 'bob', 'reports', 'User', 'alice')
|
|
466
|
-
|
|
467
|
-
const aliceReports = await provider.related('User', 'alice', 'reports')
|
|
468
|
-
const bobReports = await provider.related('User', 'bob', 'reports')
|
|
469
|
-
|
|
470
|
-
expect(aliceReports).toHaveLength(1)
|
|
471
|
-
expect(bobReports).toHaveLength(1)
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
describe('search edge cases', () => {
|
|
476
|
-
const schema = {
|
|
477
|
-
Post: {
|
|
478
|
-
title: 'string',
|
|
479
|
-
content: 'markdown',
|
|
480
|
-
},
|
|
481
|
-
} as const
|
|
482
|
-
|
|
483
|
-
it('searches for empty string', async () => {
|
|
484
|
-
const { db } = DB(schema)
|
|
485
|
-
|
|
486
|
-
await db.Post.create('post1', {
|
|
487
|
-
title: 'Test',
|
|
488
|
-
content: 'Content',
|
|
489
|
-
})
|
|
490
|
-
|
|
491
|
-
const results = await db.Post.search('')
|
|
492
|
-
|
|
493
|
-
// Empty search might return all or none - implementation dependent
|
|
494
|
-
expect(Array.isArray(results)).toBe(true)
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
it('searches for special regex characters', async () => {
|
|
498
|
-
const { db } = DB(schema)
|
|
499
|
-
|
|
500
|
-
await db.Post.create('post1', {
|
|
501
|
-
title: 'Test [brackets]',
|
|
502
|
-
content: 'Content with (parens)',
|
|
503
|
-
})
|
|
504
|
-
|
|
505
|
-
// Should not throw regex error
|
|
506
|
-
const results = await db.Post.search('[brackets]')
|
|
507
|
-
expect(Array.isArray(results)).toBe(true)
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
it('searches for very long query', async () => {
|
|
511
|
-
const { db } = DB(schema)
|
|
512
|
-
|
|
513
|
-
await db.Post.create('post1', {
|
|
514
|
-
title: 'Test',
|
|
515
|
-
content: 'Content',
|
|
516
|
-
})
|
|
517
|
-
|
|
518
|
-
const longQuery = 'word '.repeat(1000)
|
|
519
|
-
const results = await db.Post.search(longQuery)
|
|
520
|
-
|
|
521
|
-
expect(Array.isArray(results)).toBe(true)
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
it('searches with minScore 0', async () => {
|
|
525
|
-
const { db } = DB(schema)
|
|
526
|
-
|
|
527
|
-
await db.Post.create('post1', {
|
|
528
|
-
title: 'Test TypeScript',
|
|
529
|
-
content: 'Content',
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
const results = await db.Post.search('TypeScript', {
|
|
533
|
-
minScore: 0,
|
|
534
|
-
})
|
|
535
|
-
|
|
536
|
-
expect(results.length).toBeGreaterThan(0)
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
it('searches with minScore 1', async () => {
|
|
540
|
-
const { db } = DB(schema)
|
|
541
|
-
|
|
542
|
-
await db.Post.create('post1', {
|
|
543
|
-
title: 'Test',
|
|
544
|
-
content: 'Content',
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
const results = await db.Post.search('Test', {
|
|
548
|
-
minScore: 1,
|
|
549
|
-
})
|
|
550
|
-
|
|
551
|
-
// Very high threshold might return only exact matches
|
|
552
|
-
expect(Array.isArray(results)).toBe(true)
|
|
553
|
-
})
|
|
554
|
-
})
|
|
555
|
-
|
|
556
|
-
describe('pagination edge cases', () => {
|
|
557
|
-
const schema = {
|
|
558
|
-
Item: { value: 'number' },
|
|
559
|
-
} as const
|
|
560
|
-
|
|
561
|
-
beforeEach(async () => {
|
|
562
|
-
const { db } = DB(schema)
|
|
563
|
-
for (let i = 0; i < 10; i++) {
|
|
564
|
-
await db.Item.create(`item${i}`, { value: i })
|
|
565
|
-
}
|
|
566
|
-
})
|
|
567
|
-
|
|
568
|
-
it('handles offset beyond result count', async () => {
|
|
569
|
-
const { db } = DB(schema)
|
|
570
|
-
|
|
571
|
-
const results = await db.Item.list({
|
|
572
|
-
offset: 100,
|
|
573
|
-
})
|
|
574
|
-
|
|
575
|
-
expect(results).toEqual([])
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
it('handles limit of 0', async () => {
|
|
579
|
-
const { db } = DB(schema)
|
|
580
|
-
|
|
581
|
-
const results = await db.Item.list({
|
|
582
|
-
limit: 0,
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
// Limit 0 might return empty or all - implementation dependent
|
|
586
|
-
expect(Array.isArray(results)).toBe(true)
|
|
587
|
-
})
|
|
588
|
-
|
|
589
|
-
it('handles negative limit (treated as invalid)', async () => {
|
|
590
|
-
const { db } = DB(schema)
|
|
591
|
-
|
|
592
|
-
const results = await db.Item.list({
|
|
593
|
-
limit: -1,
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
// Negative limit should be handled gracefully
|
|
597
|
-
expect(Array.isArray(results)).toBe(true)
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
it('handles very large limit', async () => {
|
|
601
|
-
const { db } = DB(schema)
|
|
602
|
-
|
|
603
|
-
const results = await db.Item.list({
|
|
604
|
-
limit: 1000000,
|
|
605
|
-
})
|
|
606
|
-
|
|
607
|
-
expect(results.length).toBeLessThanOrEqual(10)
|
|
608
|
-
})
|
|
609
|
-
})
|
|
610
|
-
|
|
611
|
-
describe('type coercion', () => {
|
|
612
|
-
const schema = {
|
|
613
|
-
Mixed: {
|
|
614
|
-
str: 'string',
|
|
615
|
-
num: 'number',
|
|
616
|
-
bool: 'boolean',
|
|
617
|
-
},
|
|
618
|
-
} as const
|
|
619
|
-
|
|
620
|
-
it('stores values as provided', async () => {
|
|
621
|
-
const { db } = DB(schema)
|
|
622
|
-
|
|
623
|
-
const item = await db.Mixed.create('item1', {
|
|
624
|
-
str: 'hello',
|
|
625
|
-
num: 42,
|
|
626
|
-
bool: true,
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
expect(typeof item.str).toBe('string')
|
|
630
|
-
expect(typeof item.num).toBe('number')
|
|
631
|
-
expect(typeof item.bool).toBe('boolean')
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
it('handles null values', async () => {
|
|
635
|
-
const { db } = DB(schema)
|
|
636
|
-
|
|
637
|
-
const item = await db.Mixed.create('item1', {
|
|
638
|
-
str: 'test',
|
|
639
|
-
num: 1,
|
|
640
|
-
bool: false,
|
|
641
|
-
} as any)
|
|
642
|
-
|
|
643
|
-
expect(item.str).toBe('test')
|
|
644
|
-
})
|
|
645
|
-
})
|
|
646
|
-
})
|