import { describe, expect, it } from 'vitest' import type { BaseDocumentStorage } from '../../src/document-storage/base-document-storage.js' import { collect } from '../helpers.js' type User = { name?: string; email?: string; age?: number; tags?: string[]; logins?: number; address?: { city?: string } } /** Behaviour every document storage must have. `make` returns an empty storage. */ export function runDocumentStorageSuite(name: string, make: () => BaseDocumentStorage): void { describe(`document storage: ${name}`, () => { const users = () => make().child('users') it('inserts with time-sortable ids, gets and lists', async () => { const u = users() const a = await u.insert({ name: 'Ada' }) const b = await u.insert({ name: 'Bob' }) expect(a < b).toBe(true) expect(await u.get(a)).toEqual({ name: 'Ada' }) expect(await collect(u.keys())).toEqual([a, b]) }) it('set / get / delete in self and child forms', async () => { const u = users() await u.set('1', { name: 'Ada' }) expect(await u.child('1').get()).toEqual({ name: 'Ada' }) await u.child('1').set({ name: 'Ada L.' }) expect(await u.getMany(['1', '2'])).toEqual([{ name: 'Ada L.' }, undefined]) expect(await u.delete('1')).toBe(true) expect(await u.child('1').delete()).toBe(false) expect(await u.get('1')).toBeUndefined() await u.set('1', { name: 'Again' }) expect(await u.get('1')).toEqual({ name: 'Again' }) }) it('updates with operators, returns the new state, and supports upsert', async () => { const u = users() await u.set('1', { name: 'Ada', tags: ['a'], logins: 1 }) expect(await u.update('1', { $inc: { logins: 2 }, $push: { tags: 'b' }, $set: { 'address.city': 'London' } })).toEqual({ name: 'Ada', tags: ['a', 'b'], logins: 3, address: { city: 'London' }, }) expect(await u.child('1').update({ $unset: { address: 1 } })).toEqual({ name: 'Ada', tags: ['a', 'b'], logins: 3 }) expect(await u.update('missing', { $set: { a: 1 } })).toBeUndefined() expect(await u.update('missing', { $inc: { n: 1 } }, { upsert: true })).toEqual({ n: 1 }) }) it('rejects bad keys and documents', async () => { const u = users() await expect(u.get(undefined as never)).rejects.toThrow(TypeError) await expect(u.update(undefined as never, { $set: {} })).rejects.toThrow(TypeError) await expect(make().get()).rejects.toThrow(/root/) await expect(u.set('x', [] as never)).rejects.toThrow(/plain object/) }) it('nests documents under documents', async () => { const u = users() await u.set('1', { name: 'Ada' }) await u.child('1').child('posts').set('p1', { title: 'Hi' }) expect(await u.child('1').child('posts').get('p1')).toEqual({ title: 'Hi' }) expect((await collect(u.find())).map((r) => r.key)).toEqual(['1']) }) it('finds with equality, comparisons, $in, dot paths, sort, skip and limit', async () => { const u = users() const rows: [string, User][] = [ ['a', { name: 'Ada', age: 36, email: 'ada@x', tags: ['x', 'y'], address: { city: 'London' } }], ['b', { name: 'Bob', age: 20, email: 'bob@x', tags: ['y'] }], ['c', { name: 'Cy', age: 51, address: { city: 'London' } }], ['d', { name: 'Di', age: 20 }], ] for (const [k, v] of rows) await u.set(k, v) const keys = async (filter: Record, options = {}) => (await collect(u.find(filter, options))).map((r) => r.key) expect(await keys({ age: 20 })).toEqual(['b', 'd']) expect(await keys({ age: { $gte: 20, $lt: 40 } })).toEqual(['a', 'b', 'd']) expect(await keys({ age: { $in: [36, 51] } })).toEqual(['a', 'c']) expect(await keys({ 'address.city': 'London', age: { $gt: 40 } })).toEqual(['c']) expect(await keys({ tags: 'y' })).toEqual(['a', 'b']) expect(await keys({ email: null })).toEqual(['c', 'd']) expect(await keys({}, { sort: { age: -1 } })).toEqual(['c', 'a', 'b', 'd']) expect(await keys({}, { sort: { age: 1 }, skip: 1, limit: 2 })).toEqual(['d', 'a']) expect(await u.findOne({ email: 'bob@x' })).toEqual({ key: 'b', data: rows[1]![1] }) expect(await u.findOne({ email: 'nobody' })).toBeUndefined() }) }) }