refactor(session-query): simplify persistence binding (round 1)

This commit is contained in:
Hypatia May
2026-07-16 13:55:23 +08:00
parent 97e5f43f8b
commit 4505c6c55a
2 changed files with 48 additions and 39 deletions

View File

@@ -326,6 +326,24 @@ describe('SQLite session search', () => {
})).rejects.toThrow(expectCode('SESSION_QUERY_STALE_CURSOR'))
})
it('invalidates session cursors after transient persistence topology changes', async () => {
TestPersistence.reset()
const ctx = await liveContext({ path: ':memory:', defaultLimit: 1, maxLimit: 5 })
ctx.sessions.create(SessionId('first'), { seed: messageEvents('needle first') })
ctx.sessions.create(SessionId('second'), { seed: messageEvents('needle second') })
const page = await ctx.sessionSearch.searchSessions({ query: 'needle', limit: 1 })
if (page.nextCursor === undefined) throw new Error('expected cursor')
const persistence = await ctx.plugin(TestPersistence)
await persistence.dispose()
await expect(ctx.sessionSearch.searchSessions({
query: 'needle',
limit: 1,
cursor: page.nextCursor,
})).rejects.toThrow(expectCode('SESSION_QUERY_STALE_CURSOR'))
})
it('rejects invalid requests, filters, cursors, and direct config', async () => {
const ctx = await liveContext({ path: ':memory:', defaultLimit: 2, maxLimit: 3 })
const session = ctx.sessions.create(SessionId('valid'), { seed: messageEvents('needle') })
@@ -503,11 +521,6 @@ describe('SQLite reconciliation and source lifecycle', () => {
TestPersistence.set({ meta: durable, events: messageEvents('new needle') })
TestPersistence.revisions.set(durable.id, revision)
const replacement = await ctx.plugin(TestPersistence)
const internals = ctx.sessionSearch as unknown as {
_lastPersistenceRevision: number
_persistenceRevision: number
}
expect(internals._persistenceRevision).not.toBe(internals._lastPersistenceRevision)
const page = await ctx.sessionSearch.searchSessions({ query: 'new needle' })
expect(TestPersistence.loads.get(durable.id)).toBe(2)
expect(page).toMatchObject({ items: [{ header: durable }] })
@@ -553,13 +566,15 @@ describe('SQLite reconciliation and source lifecycle', () => {
TestPersistence.reset([{ meta: durable, events: messageEvents('durable needle') }])
const ctx = await liveContext()
await ctx.plugin(TestPersistence)
const internals = ctx.sessionSearch as unknown as { _persistenceRevision: number }
const internals = ctx.sessionSearch as unknown as {
_persistenceBinding: { service?: SessionPersistence }
}
const originalList = ctx.sessions.list.bind(ctx.sessions)
let bumped = false
const list = vi.spyOn(ctx.sessions, 'list').mockImplementation(() => {
if (!bumped) {
bumped = true
internals._persistenceRevision += 1
internals._persistenceBinding = { ...internals._persistenceBinding }
}
return originalList()
})