test(session): cover the request/context fold and its turn enclosure
Adds direct coverage for `Session.requestContext()`: undefined before any record, folding a seeded log on first read (the watermark starts at 0 with the seed already in the log, so a first read must consume all of it), incremental advance across appends, a batch appended between two reads, and the frozen return that keeps a reader from desyncing later dedup comparisons. The turn-enclosure assertion goes in the invariant spec rather than beside the fold tests, because enclosure is enforced by the opt-in invariant companion plugin and not by `append` itself. Asserting it on a bare Session would have been a test that passes for the wrong reason.
This commit is contained in:
@@ -144,6 +144,12 @@ describe('session-log invariants', () => {
|
|||||||
source: { kind: 'user' },
|
source: { kind: 'user' },
|
||||||
}),
|
}),
|
||||||
}, { surfaceOp: 'append' })).toThrow(/outside any open turn/)
|
}, { surfaceOp: 'append' })).toThrow(/outside any open turn/)
|
||||||
|
// Route capacity is core execution state like the header beside it.
|
||||||
|
expect(() => outside.append('request/context', {
|
||||||
|
provider: 'mock',
|
||||||
|
model: 'm',
|
||||||
|
contextWindow: 128_000,
|
||||||
|
})).toThrow(/outside any open turn/)
|
||||||
// The owning plugin decides whether a merge-extensible event is log-only.
|
// The owning plugin decides whether a merge-extensible event is log-only.
|
||||||
const appendUnknown = outside.append.bind(outside) as (type: string, data: unknown) => unknown
|
const appendUnknown = outside.append.bind(outside) as (type: string, data: unknown) => unknown
|
||||||
expect(() => { appendUnknown('plugin/marker', {}) }).not.toThrow()
|
expect(() => { appendUnknown('plugin/marker', {}) }).not.toThrow()
|
||||||
|
|||||||
@@ -91,3 +91,58 @@ describe('legacy request-header format', () => {
|
|||||||
expect(session.events).toHaveLength(0)
|
expect(session.events).toHaveLength(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Session.requestContext', () => {
|
||||||
|
const CAPACITY = { provider: 'mock', model: 'm', contextWindow: 128_000 }
|
||||||
|
|
||||||
|
/** A turn-enclosed capacity record; the invariant rejects one outside a turn. */
|
||||||
|
function seedWith(...records: { provider: string; model: string; contextWindow: number }[]): SessionEvent[] {
|
||||||
|
const events: SessionEvent[] = [{
|
||||||
|
type: 'turn/start', seq: 0, time: 1, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } },
|
||||||
|
}]
|
||||||
|
for (const data of records) {
|
||||||
|
events.push({ type: 'request/context', seq: events.length, time: 1, data })
|
||||||
|
}
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
|
it('reads undefined before any record exists', () => {
|
||||||
|
expect(new Session(SessionId('no-capacity')).requestContext()).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('folds a seeded log on first read, taking the last record', () => {
|
||||||
|
// The fold watermark starts at 0 with the seed already in the log, so the
|
||||||
|
// first read must consume the whole seed rather than skip it.
|
||||||
|
const session = new Session(SessionId('seeded-capacity'), seedWith(
|
||||||
|
CAPACITY,
|
||||||
|
{ ...CAPACITY, model: 'later', contextWindow: 256_000 },
|
||||||
|
))
|
||||||
|
expect(session.requestContext()).toEqual({ provider: 'mock', model: 'later', contextWindow: 256_000 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('advances incrementally across appends and skips unrelated events', () => {
|
||||||
|
const session = new Session(SessionId('incremental-capacity'), seedWith(CAPACITY))
|
||||||
|
expect(session.requestContext()).toEqual(CAPACITY)
|
||||||
|
session.append('todo/write', { todos: [] })
|
||||||
|
expect(session.requestContext()).toEqual(CAPACITY)
|
||||||
|
session.append('request/context', { ...CAPACITY, model: 'next', contextWindow: 64_000 })
|
||||||
|
expect(session.requestContext()).toEqual({ provider: 'mock', model: 'next', contextWindow: 64_000 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('folds a batch appended between two reads', () => {
|
||||||
|
const session = new Session(SessionId('batched-capacity'), seedWith(CAPACITY))
|
||||||
|
expect(session.requestContext()).toEqual(CAPACITY)
|
||||||
|
session.append('request/context', { ...CAPACITY, contextWindow: 200_000 })
|
||||||
|
session.append('todo/write', { todos: [] })
|
||||||
|
session.append('request/context', { ...CAPACITY, contextWindow: 300_000 })
|
||||||
|
expect(session.requestContext()?.contextWindow).toBe(300_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exposes a frozen record so a reader cannot desync later comparisons', () => {
|
||||||
|
const session = new Session(SessionId('frozen-capacity'), seedWith(CAPACITY))
|
||||||
|
const held = session.requestContext()
|
||||||
|
if (held === undefined) throw new Error('expected a folded capacity record')
|
||||||
|
expect(Object.isFrozen(held)).toBe(true)
|
||||||
|
expect(() => { (held as { contextWindow: number }).contextWindow = 1 }).toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user