Files
Claude e27541c46a
CI / Node 20 (push) Successful in 1m49s
CI / Node 22 (push) Successful in 1m52s
Add Prettier formatting and Gitea Actions CI (format, typecheck, test, build)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
2026-10-23 07:29:38 +00:00

124 lines
4.9 KiB
TypeScript

import { describe, expectTypeOf, it } from 'vitest'
import { KVStorage, type KVNode } from '../src/kv/node.js'
import { InMemoryKVDriver } from '../src/kv/memory.js'
import { KVEventStorage } from '../src/events/kv.js'
import type { StoredEvent } from '../src/events/types.js'
import { EventSourcedDocumentStorage } from '../src/documents/event-sourced.js'
import type { FindResult } from '../src/documents/types.js'
import type { AnySpec, Fields, Many } from '../src/types/schema.js'
type User = { username: string; age: number }
type Post = { title: string; createdAt: string }
type Schema = {
children: {
users: Many<User, { posts: Many<Post> }>
profile: { children: { [id: string]: { children: Fields<User> } } } // field-per-key style
}
}
const driver = new InMemoryKVDriver()
describe('KV types (section 7)', () => {
const db = new KVStorage<Schema>(driver)
it('reads child values at dynamic levels', () => {
expectTypeOf(db.child('users').get('123')).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(db.child('users').child('123').get()).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(db.child('users').getMany(['1', '2'])).toEqualTypeOf<Promise<(User | undefined)[]>>()
})
it('navigates named levels', () => {
expectTypeOf(db.child('users').child('123').child('posts')).toEqualTypeOf<KVNode<Many<Post>>>()
// @ts-expect-error unknown named child
db.child('users').child('123').child('postz')
// @ts-expect-error unknown top-level child
db.child('userz')
})
it('reads field-per-key values', () => {
expectTypeOf(db.child('profile').child('9').get('age')).toEqualTypeOf<Promise<number | undefined>>()
expectTypeOf(db.child('profile').child('9').get('username')).toEqualTypeOf<Promise<string | undefined>>()
// @ts-expect-error unknown field
db.child('profile').child('9').get('agee')
})
it('checks written values', () => {
db.child('users').set('1', { username: 'a', age: 1 })
db.child('users').child('1').set({ username: 'a', age: 1 })
// @ts-expect-error wrong value type
db.child('users').set('1', { username: 'a' })
db.child('profile')
.child('9')
.setMany([
['age', 3],
['username', 'x'],
])
db.child('profile')
.child('9')
.setMany([
// @ts-expect-error value doesn't match its key
['age', 'x'],
])
})
it('types listings', () => {
expectTypeOf(db.child('users').keys()).toEqualTypeOf<AsyncGenerator<string>>()
expectTypeOf(db.child('profile').child('9').keys()).toEqualTypeOf<AsyncGenerator<'username' | 'age'>>()
expectTypeOf(db.child('users').entries()).toEqualTypeOf<AsyncGenerator<[string, User]>>()
})
it('defaults to untyped and supports as<S>()', () => {
const raw = new KVStorage(driver)
expectTypeOf(raw.child('anything').child(5).get('x')).toEqualTypeOf<Promise<unknown>>()
const users = raw.child('users').as<Many<User>>()
expectTypeOf(users.get('1')).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(raw).toMatchTypeOf<KVNode<AnySpec>>()
})
})
type OrderEvent = { type: 'created'; total: number } | { type: 'paid' }
type EventSchema = { children: { orders: Many<OrderEvent, { shipments: Many<{ carrier: string }> }> } }
describe('event types', () => {
const events = new KVEventStorage<EventSchema>(new KVStorage(driver))
const orders = events.child('orders')
it('checks appended events', () => {
orders.append('o1', [{ type: 'created', total: 40 }])
orders.child('o1').append([{ type: 'paid' }])
// @ts-expect-error not an OrderEvent
orders.append('o1', [{ type: 'refunded' }])
orders
.child('o1')
.child('shipments')
.append('s1', [{ carrier: 'x' }])
})
it('types read results', () => {
expectTypeOf(orders.read('o1')).toEqualTypeOf<AsyncIterable<StoredEvent<OrderEvent>>>()
expectTypeOf(orders.version('o1')).toEqualTypeOf<Promise<number>>()
})
})
type DocSchema = { children: { users: Many<User, { posts: Many<Post> }> } }
describe('document types', () => {
const docs = new EventSourcedDocumentStorage<DocSchema>(new KVEventStorage(new KVStorage(driver)))
const users = docs.child('users')
it('types reads and writes', () => {
expectTypeOf(users.get('1')).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(users.child('1').get()).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(users.insert({ username: 'a', age: 1 })).toEqualTypeOf<Promise<string>>()
// @ts-expect-error missing field
users.insert({ username: 'a' })
expectTypeOf(users.update('1', { $inc: { age: 1 } })).toEqualTypeOf<Promise<User | undefined>>()
expectTypeOf(users.child('1').child('posts').get('p')).toEqualTypeOf<Promise<Post | undefined>>()
})
it('types queries', () => {
expectTypeOf(users.find({ age: { $gte: 18 } })).toEqualTypeOf<AsyncGenerator<FindResult<User>>>()
expectTypeOf(users.findOne()).toEqualTypeOf<Promise<FindResult<User> | undefined>>()
})
})