Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { createIdGenerator } from '../../src/documents/ulid.js'
|
|
import { matches } from '../../src/documents/filter.js'
|
|
import { applyPatch, resolveUpdate } from '../../src/documents/update.js'
|
|
|
|
describe('ulid', () => {
|
|
it('is strictly increasing within one millisecond and sorts by time', () => {
|
|
let t = 1_700_000_000_000
|
|
const gen = createIdGenerator(() => t)
|
|
const ids = Array.from({ length: 1000 }, () => gen())
|
|
expect([...ids].sort()).toEqual(ids)
|
|
expect(new Set(ids).size).toBe(1000)
|
|
t += 1
|
|
const later = gen()
|
|
expect(later > ids.at(-1)!).toBe(true)
|
|
expect(later.slice(0, 10) > ids[0]!.slice(0, 10)).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('filter semantics', () => {
|
|
it('requires one array element to satisfy all positive operators of a condition', () => {
|
|
expect(matches({ s: [1, 20] }, { s: { $gt: 5, $lt: 10 } })).toBe(false) // MongoDB would match
|
|
expect(matches({ s: [1, 7] }, { s: { $gt: 5, $lt: 10 } })).toBe(true)
|
|
expect(matches({ s: [1, 20] }, { $and: [{ s: { $gt: 5 } }, { s: { $lt: 10 } }] })).toBe(true)
|
|
})
|
|
|
|
it('traverses arrays of sub-documents on dot paths', () => {
|
|
expect(matches({ a: [{ b: 1 }, { b: 2 }] }, { 'a.b': 2 })).toBe(true)
|
|
expect(matches({ a: [{ b: 1 }] }, { 'a.0.b': 1 })).toBe(true)
|
|
expect(matches({ a: [{ b: 1 }] }, { 'a.b': { $exists: true } })).toBe(true)
|
|
expect(matches({ a: [] }, { 'a.b': { $exists: true } })).toBe(false)
|
|
})
|
|
|
|
it('treats null as matching missing', () => {
|
|
expect(matches({}, { x: null })).toBe(true)
|
|
expect(matches({ x: null }, { x: { $ne: null } })).toBe(false)
|
|
expect(matches({ x: 0 }, { x: { $ne: null } })).toBe(true)
|
|
})
|
|
|
|
it('rejects unknown operators', () => {
|
|
expect(() => matches({}, { x: { $regex: 'a' } })).toThrow(/Unknown operator/)
|
|
expect(() => matches({}, { $nor: [] } as never)).toThrow(/Unknown top-level/)
|
|
})
|
|
})
|
|
|
|
describe('update resolution', () => {
|
|
it('$pull by sub-document filter and by condition', () => {
|
|
const state = {
|
|
items: [
|
|
{ k: 1, v: 'a' },
|
|
{ k: 2, v: 'b' },
|
|
],
|
|
n: [1, 5, 9],
|
|
}
|
|
const patch = resolveUpdate(state, { $pull: { items: { k: 2 }, n: { $gte: 5 } } })
|
|
expect(applyPatch(state, patch)).toEqual({ items: [{ k: 1, v: 'a' }], n: [1] })
|
|
})
|
|
|
|
it('$unset of an array position nulls it', () => {
|
|
expect(applyPatch({ a: [1, 2] }, resolveUpdate({ a: [1, 2] }, { $unset: { 'a.0': true } }))).toEqual({ a: [null, 2] })
|
|
})
|
|
})
|