mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
test(mobile): pin the replay contiguity-scan rejection branches
The scan's three rejection rules were unpinned: deleting the `sawNewMessage` guard, the ordering check, or the retained-tail anchor each left the whole suite green. Cover the interleaved-new-row, reordered-id, and short-of-tail cases, and the replaced-window hasMore fallback. Each new test is mutation-proven to kill exactly one mutant.
This commit is contained in:
@@ -133,19 +133,64 @@ describe('applyMobileNativeChatStreamFrame', () => {
|
||||
const merger = createNativeChatMerger()
|
||||
replaceList(merger, ['old-1', 'shared', 'old-tail'].map(message))
|
||||
|
||||
// `hasMore: true` so the authoritative-removal rule can't short-circuit —
|
||||
// the contiguity scan itself must reject the interleaved new message.
|
||||
const replay = [message('shared'), message('compacted-summary'), message('old-tail')]
|
||||
expect(
|
||||
applyMobileNativeChatStreamFrame({
|
||||
merger,
|
||||
frame: { type: 'snapshot', messages: replay, hasMore: false, beforeOffset: 0 },
|
||||
frame: { type: 'snapshot', messages: replay, hasMore: true, beforeOffset: 9 },
|
||||
limit: 100,
|
||||
replaceSnapshot: false
|
||||
})
|
||||
).toEqual({
|
||||
kind: 'messages',
|
||||
messages: replay,
|
||||
hasMore: false,
|
||||
beforeOffset: 0,
|
||||
hasMore: true,
|
||||
beforeOffset: 9,
|
||||
windowReplaced: true
|
||||
})
|
||||
})
|
||||
|
||||
it('replaces a replay that repeats retained ids out of order', () => {
|
||||
const merger = createNativeChatMerger()
|
||||
replaceList(merger, ['a', 'b', 'c'].map(message))
|
||||
|
||||
const replay = [message('a'), message('c'), message('b')]
|
||||
expect(
|
||||
applyMobileNativeChatStreamFrame({
|
||||
merger,
|
||||
frame: { type: 'snapshot', messages: replay, hasMore: true, beforeOffset: 4 },
|
||||
limit: 100,
|
||||
replaceSnapshot: false
|
||||
})
|
||||
).toEqual({
|
||||
kind: 'messages',
|
||||
messages: replay,
|
||||
hasMore: true,
|
||||
beforeOffset: 4,
|
||||
windowReplaced: true
|
||||
})
|
||||
})
|
||||
|
||||
it('replaces a replay that stops short of the retained newest row', () => {
|
||||
const merger = createNativeChatMerger()
|
||||
replaceList(merger, ['a', 'b', 'c'].map(message))
|
||||
|
||||
// Merging would keep 'c', a row the replayed window no longer carries.
|
||||
const replay = [message('a'), message('b')]
|
||||
expect(
|
||||
applyMobileNativeChatStreamFrame({
|
||||
merger,
|
||||
frame: { type: 'snapshot', messages: replay, hasMore: true, beforeOffset: 1 },
|
||||
limit: 100,
|
||||
replaceSnapshot: false
|
||||
})
|
||||
).toEqual({
|
||||
kind: 'messages',
|
||||
messages: replay,
|
||||
hasMore: true,
|
||||
beforeOffset: 1,
|
||||
windowReplaced: true
|
||||
})
|
||||
})
|
||||
|
||||
@@ -311,6 +311,28 @@ describe('useMobileNativeChatSession', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('clears hasMore when a replaced window is shorter than the initial page', async () => {
|
||||
// A replaced window without paging metadata is judged by its own length:
|
||||
// shorter than a full page means the whole transcript is on screen.
|
||||
const sendRequest = vi.fn()
|
||||
const subscribe: RpcClient['subscribe'] = vi.fn((_method, _params, onData) => {
|
||||
emit = onData
|
||||
onData({
|
||||
type: 'snapshot',
|
||||
messages: Array.from({ length: 40 }, (_unused, index) => message(`win-${index}`)),
|
||||
hasMore: true,
|
||||
beforeOffset: 100
|
||||
})
|
||||
return () => {}
|
||||
})
|
||||
await mount({ sendRequest, subscribe } as unknown as RpcClient)
|
||||
expect(state?.hasMore).toBe(true)
|
||||
|
||||
await act(async () => emit({ type: 'replacement', messages: [message('only')] }))
|
||||
|
||||
expect(state?.hasMore).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects a cursor page invalidated by live trim and retries with a growing tail', async () => {
|
||||
let resolveCursorPage: (response: unknown) => void = () => {}
|
||||
const sendRequest = vi
|
||||
|
||||
Reference in New Issue
Block a user