Files
PotentialStorage/test/utils/utils.test.ts
T
Claude 08fb78721f
CI / Node 20 (pull_request) Successful in 10s
CI / Node 22 (pull_request) Successful in 11s
Simplify: one abstract base class per layer
- BaseKVStorage / BaseEventStorage / BaseDocumentStorage define the API once;
  implementations provide only raw primitives (client and server share an API)
- Drop KV indexes, query planner, unique indexes; document stores take index fields
- Escape every char <= '/' so child keys stay contiguous; skip-scan is now simple
- Event-sourced documents = event storage + any document store for projections
- New folder layout (kv-storage, event-storage, document-storage, utils); tests only in test/

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
2026-10-23 08:05:03 +00:00

98 lines
4.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import fc from 'fast-check'
import { Keys } from '../../src/utils/keys.js'
import { SortedMap } from '../../src/utils/sorted-map.js'
import { Ulid } from '../../src/utils/ulid.js'
import { DocumentQuery } from '../../src/utils/document-query.js'
import { DocumentUpdate } from '../../src/utils/document-update.js'
describe('Keys', () => {
it('round-trips any segment and never emits a character at or below the separator', () => {
fc.assert(
fc.property(fc.string({ unit: 'binary', minLength: 1 }), (s) => {
const escaped = Keys.escape(s)
expect(Keys.unescape(escaped)).toBe(s)
expect([...escaped].every((c) => c > '/')).toBe(true)
expect(Keys.isReserved(escaped)).toBe(false)
}),
)
})
it('keeps every child subtree contiguous in key order', () => {
fc.assert(
fc.property(
fc.uniqueArray(fc.string({ unit: fc.constantFrom('a', '-', '.', '/', '~', ' ', '0'), minLength: 1, maxLength: 3 }), { minLength: 2 }),
(segments) => {
const keys = segments.flatMap((s) => [Keys.escape(s), Keys.escape(s) + '/x']).sort()
const order = keys.map((k) => k.split('/')[0])
// Each segment's keys appear as one consecutive run.
expect(new Set(order).size).toBe(order.filter((s, i) => s !== order[i - 1]).length)
},
),
)
})
})
describe('SortedMap', () => {
it('matches a sorted reference, including iteration across mutations', () => {
fc.assert(
fc.property(
fc.array(fc.tuple(fc.boolean(), fc.string({ unit: fc.constantFrom('a', 'b', 'c'), maxLength: 4 })), { maxLength: 200 }),
fc.boolean(),
(ops, reverse) => {
const map = new SortedMap<number>(4)
const ref = new Set<string>()
for (const [add, key] of ops) add ? (map.set(key, 1), ref.add(key)) : (map.delete(key), ref.delete(key))
const sorted = [...ref].sort()
expect([...map.keys(undefined, undefined, reverse)]).toEqual(reverse ? sorted.reverse() : sorted)
// Deleting each key as we pass it must not skip the rest.
const seen = [...map.keys()].length
let visited = 0
for (const key of map.keys()) (visited++, map.delete(key))
expect(visited).toBe(seen)
},
),
)
})
})
describe('Ulid', () => {
it('is strictly increasing within a millisecond and sorts by time', () => {
let t = 1_700_000_000_000
const ulid = new Ulid(() => t)
const ids = Array.from({ length: 500 }, () => ulid.generate())
expect([...ids].sort()).toEqual(ids)
t++
expect(ulid.generate() > ids.at(-1)!).toBe(true)
})
})
describe('DocumentQuery', () => {
const q = (filter: object, doc: object) => new DocumentQuery(filter as never).matches(doc as never)
it('matches equality, comparisons, $in, dot paths, arrays and null-as-missing', () => {
expect(q({ a: 1 }, { a: 1 })).toBe(true)
expect(q({ 'a.b': 'x' }, { a: { b: 'x' } })).toBe(true)
expect(q({ n: { $gte: 2, $lt: 5 } }, { n: 4 })).toBe(true)
expect(q({ n: { $gt: 2 } }, { n: '9' })).toBe(false)
expect(q({ n: { $in: [1, 2] } }, { n: 2 })).toBe(true)
expect(q({ tags: 'x' }, { tags: ['y', 'x'] })).toBe(true)
expect(q({ tags: ['x'] }, { tags: ['x'] })).toBe(true)
expect(q({ a: null }, {})).toBe(true)
expect(() => q({ a: { $regex: 'x' } }, {})).toThrow(/Unsupported/)
})
})
describe('DocumentUpdate', () => {
it('resolves operators to final values', () => {
const state = { n: 5, tags: ['a', 'b'], nested: { x: 1 } }
const change = DocumentUpdate.resolve(state, { $inc: { n: 1 }, $push: { tags: { $each: ['c'] } }, $unset: { 'nested.x': 1 }, $set: { 'nested.y': 2 } })
expect(change).toEqual({ op: 'patch', set: { n: 6, tags: ['a', 'b', 'c'], 'nested.y': 2 }, unset: ['nested.x'] })
expect(DocumentUpdate.apply(state, change)).toEqual({ n: 6, tags: ['a', 'b', 'c'], nested: { y: 2 } })
expect(DocumentUpdate.apply(state, DocumentUpdate.resolve(state, { $pull: { tags: 'a' }, $addToSet: { more: { $each: [1, 1] } } }))).toMatchObject({
tags: ['b'],
more: [1],
})
expect(() => DocumentUpdate.resolve(state, { $set: { n: 1 }, $inc: { n: 1 } })).toThrow(/twice/)
})
})